1. 不规则窗口的创建

    方法一:
    让图片的背景色与显示部分的颜色明显不同,将 FormBorderStyle 属性设置为 None。
    将窗体的 BackgroundImage 属性设置为先前创建的位图文件。 设置窗体的 BackColor 图片
    背景色,在窗体的构造函数里添加 this.TransparencyKey = this.BackColor; 一切OK。
   
    缺点:1) 不能胜任24位色以上环境。实际上,即使16色的环境,效果也不理想,图片边缘的阴影
             显示为窗体背景。不可能对图片进行任意放大。
          2) 图片边缘锯齿明显。
         
    方法二:
    采用无Alpha通道的位图图片,通过扫描图片的每一点,取出与边缘颜色不同的所以像素,合并到
    GraphicsPath中,然后使用这个 GraphicsPath 创建一个 Region并赋给窗体。代码如下:

 1    }
    这种方法除了可以解决方法一中的不胜任24色以上环境的问题外,与方法一的缺点是一样的。想使用带有阴影
    的图片也是不可能的。
   
    方法三(最优解):
    这种方法是这个软件最后采用的方法。主要利用 Win32 API 函数 UpdateLayeredWindow 来完成。听起来很简单的
    样子,实际上要做的工作是不少的。首先要设置 Window 的ExStyle支持 WS_EX_LAYERED,这可以通过 GetWindowLog
    和 SetWindowLong API实现,也可以重载 Form 的 CreateParams 属性。如下:
    protected override CreateParams CreateParams {
        get {
            CreateParams createParams = base.CreateParams;
            createParams.ExStyle |= PInvokeService.WS_EX_LAYERED;
            return createParams;
        }
    }
   
    其中 PInvokeService.WS_EX_LAYERED 的值是 0x80000
    UpdateLayeredWindow API 也比较复杂,在 C#里调用也不方便,所以还是写在一个 class 里面吧,另外还要绘制
    时钟的指针和其他一些东西,这个是不能在 直接重载 Form 的 OnPaint或者处理 Paint事件了,如果你这样做,你
    会发现是没有效果的。所以干脆把相关的东西先列出来吧,这里面可能有一些东西跟这个主题无关,但是也不删除了:

  1我的 WinClock 项目系列之二    // PInvokeService.cs
  2    }
    下面的 WindowShapeMaker 类负责窗体形状的创建,实际中,RefreshWindow每隔一秒钟就会调用
    一次,以刷新时间。图片是具有 Alpha 通过的 32bpp bitmap, 一般为 PNG 格式。在刷新前,首先
    处理这个图片,将传入的图片做一个拷贝,这样一方便是可以根据程序设置缩放图片,一方面是保证
    源图片不会被更改。从这个图片创建一个 Graphics 对象,然后在上面画出指针以及其他必要的内容,
    最后调用 UpdateLayeredWindow 更新窗体。这里面用到很多 GDI 的操作,如下:

  1我的 WinClock 项目系列之二    // WindowShapeMaker.cs
  2    }
    上面用到的 ClockHand 类专门负责绘制时钟的指针,我们假定所有的背景图片都是对称图形,也就是中心一定在图片中心。
    指针一般要启用反锯齿,因为除了水平或者垂直的线段外,不启用反锯齿的话效果是相当差的。如下:
 1    }

    上面的 ClockOption 类保存的是应用程序的设置,如下:
  1我的 WinClock 项目系列之二    [Serializable()]
  2    }
    IMementoCapable 接口是很明显是一个备忘录,有 CreateMemento() 和 SetMemento(Properties properties)
    两个方法,这次先不讲这个内容。Properties 类也有些复杂,它和IMementoCapable合起来是持久化存储的基础,
    实现比.Net序列化更为灵活的持久化存储方式。熟悉SharpDevelop的朋友可能比较清楚,这也不是本次要讨论
    的内容。

2. 总在最前
    这个比较简单,直接设置 Form 的 TopMost 属性即可。

3.使用鼠标移动钟面。
   方法一:消息方式:

 1    }

   缺点是不易控制窗体移动的范围,因此不能提供钟面只在屏幕范围内活动的选项。没有采用这种方法。
  
   方法二:重载 OnMouseDown 和 OnMouseMove(这是最后采用的方法):

 1    }

    clockOpt.CheckBounds 表示是否要检查屏幕边界,即是否只允许在屏幕范围内移动钟面。

4.鼠标穿透

 1我的 WinClock 项目系列之二    // PenetrateService.cs
 2    }

    注释掉的几行代码是有原因的,在设置了窗体的 WS_EX_LAYERED Style 以后,不能再要这两句,否则这个 Style 失去作用。
    如果没有采用这种方式,则需要加上这两句代码。

5. 窗体透明度
   
    你可能最快想到的是直接设置 Form的 Opacity 属性,但是在这里他失效了,不但不起作用,还会使WS_EX_LAYERED失效。
    其实在 UpdateLayeredWindow 的调用中,就有透明度的选项的。那句
    blend.SourceConstantAlpha = clockOpt.PreviewOpacity;
    正是这个作用。由于要支持鼠标经过时的透明度和 正常的透明度,所以ClockOption 里面还有 PreviewOpacity 这个属性。
   
最后补充一点,今天对源代码做了一些修改,今天添加了多国语言支持, 添加了中文资源,修正了农历算法问题. 添加了对允许
拖动到屏幕以外的选项. Fix了一些小的Bug. 如果你感兴趣,可以重新下载

    好了,至此这次写的也差不多了,好累, 不知道有没有漏写什么东西,唉, 时间也不早了,休息吧^_^。

参考资料:

C# winform中不规则窗体制作的解决方案(已经解决24位色以上不能正常显示问题)
用PNG透明图片和GDI+做不规则透明窗体
这是微软技术的一贯特点,使用简单。但是如果要深入的话,还是要投入不少精力的

相关文章:

  • 2022-12-23
  • 2022-01-23
  • 2021-11-12
  • 2021-07-28
  • 2021-11-25
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-07-15
  • 2022-01-06
  • 2021-07-28
  • 2022-02-20
  • 2022-12-23
相关资源
相似解决方案