Winform自定义窗体与控件的外观形状

来源:互联网 发布:冷轩网络 编辑:程序博客网 时间:2024/04/29 04:40

在开发的时候,有时我们需要自定义窗体,各种奇怪的形状都有可能,这里举一个利用Region自定义外观的例子。下图就是一个自定义十字形的窗体,和一个居中显示的蓝色自定义外形的按钮。


代码可以分为两个部分,一个是绘制按钮的,一个是绘制窗体的。窗体外形定义比较简单,只是用了四行代码,当然,这也是因为外观简单而已;其余的代码都是为自定义按钮服务的。

    public class SpecialRegionForm : Form    {        public SpecialRegionForm()        {            this.FormBorderStyle = FormBorderStyle.None;            Region btnRegion = null;            GraphicsPath path = null;            path = new GraphicsPath();            path.AddEllipse(0, 0, 100, 100);            btnRegion = new Region(path);            path = new GraphicsPath();            path.AddRectangle(new Rectangle(25, 25, 50, 50));            btnRegion.Exclude(new Region(path));            path = new GraphicsPath();            path.AddLines(new Point[]{new Point(50, 25), new Point(25, 50), new Point(50, 75), new Point(75, 50)});            btnRegion.Union(new Region(path));                        path = new GraphicsPath();            path.AddRectangle(new Rectangle(50, 0, 50, 1));            //path.AddLine(50, 0, 100, 0);            btnRegion.Union(new Region(path));            Button testButton = new Button();            testButton.Location = new Point(100, 100);            testButton.Width = 100;            testButton.Height = 100;            testButton.Text = "^o^";            testButton.Region = btnRegion;            testButton.BackColor = Color.Blue;            testButton.Cursor = Cursors.Hand;            testButton.Click += new EventHandler(delegate(object sender, System.EventArgs e) {                if (DialogResult.Yes.Equals(MessageBox.Show("Are you sure to exit?", "Exit?", MessageBoxButtons.YesNo)))                {                    this.Close();                }            });            this.Controls.Add(testButton);            Region vertical = new Region(new Rectangle(100, 0, 100, 300));            Region horizontal = new Region(new Rectangle(0, 100, 300, 100));            vertical.Union(horizontal);            this.Region = vertical;        }    }
我们先从简单的窗体外观自定义入手,由于窗体本身的“非客户区域”(标题栏、菜单栏)会占用我们常用的“客户区域”,所以在构造函数的第一行我们就把外形样式设置成NONE,这样就不会受“非客户区域”的干扰了。现在我们集中观察最后四行代码,首先需要定义一个横向居中的垂直矩形,然后再定义一个纵向居中的水平矩形,最后利用一个“并”——Union操作,把两个矩形合并,得到一个合并后的区域。把这个合并后的区域赋值给当前窗体后,就可以让窗体按照我们设计的区域进行显示了。
窗体定义完毕,下面该轮到按钮了,这个过程与窗体定义基本类似,显示定义自定义显示区域,然后再把显示区域赋值给按钮,但是需要注意一点,由于按钮本身大小的默认值远远小于我们给出的自定义区域面积,所以我们需要适当地加大按钮的宽和高。另外为了显示明显,按钮的背景颜色和鼠标光标也被定制了(蓝色+手势)。因为窗体样式为None,所以我们应该提供一个退出的方法,所以需要绑定一个Click事件为了退出当前程序。OK,这些内容都了解完之后,我们就可以把重点放在外观自定义上了。从上面的截图可以看出,按钮主要有两个部分组成,一个铜钱加一个旋转45度的正方形。看到这里,我们可能会想到,这和前面的窗体完全是两回事,不是简简单单的两个矩形就能搞得定的(多了个圈圈嘛:)。

对于复杂形状来说,我们可以用两种方法来达到目的,一是Region提供的Union、Intersect、Exclude、Xor和Complement,另一个则是用了绘制各种复杂图像的GraphicsPath对象,并且,.Net还允许我们把这两者结合起来使用。从上面的代码中可以看出,首先我们定义了一个圆形的区域,具体操作是利用定义好的GraphicsPath对象创建圆形,然后把GraphicsPath对象转成Region对象;接下来再定义一个正方形,并在当前已经生成的圆形区域基础上,利用Region.Exclude方法把矩形区域剔除;第三步则是要加入一个旋转45度的矩形,这里有两种实现方法,一种是创建一个矩形,然后GraphicsPath.Transform来进行旋转(可以参考http://www.c-sharpcorner.com/UploadFile/mgold/TransformswithGDIplus09142005064919AM/TransformswithGDIplus.aspx);另一种做法就是自己算好尺寸坐标,通过Graphics.DrawLines绘制,这里我们采用的就是这种做法。当第三个区域也绘制好之后,我们就可以把它Union到之前做好的btnRegion了。为了测试单个像素高的内容是否也能有效地参与hit testing(碰撞测试),可以添加一个高度为1像素,宽度为50像素的矩形。最后再把合并了四个(圆-矩+矩+单像素矩)图形的btnRegion指派给testButtion.Region。到这里,我们就完成了自定义按钮外观的定制工作了。

测试的时候需要对镂空区域和非镂空区域进行检测,另外还应该对我们最后加入的单像素矩形进行测试,只要鼠标移到非镂空的蓝色区域和右上角的黑线,鼠标都会变成手势图案,点击时也都会提示我们退出程序。请注意上面代码中注释掉的一行,因为我最初的想法是测试一个像素高的矩形,所以想当然地使用了Line,结果当然是不起作用了,从字面含义理解Region,当然是区域而非线条了。