c#皮肤美化:MainForm窗体

做了许多的用户控件,现在让我们换换口味,开始窗体的制作吧!这个窗体的制作可以说是整个美化中比较重要的一部分,因为她显示的是整个美化的窗体。而且内容也比较多,所以我会分几篇把她讲完,而且窗体制作的时候会和前面控件将的时候不一样,我不会在第一篇把最后的代码就放出来,而是希望做成跟着我一起一个版本一版本的完成不断的修改不断的发现问题并去完善。这也是我做这个时候的一个思路:先做一个大致的框架,然后在其基础上增加功能或者发现问题,最后完成功能和解决问题。不知道大家感觉这样是不是更好一点呢? 

好了,不多说了,先看最终的效果图: 

怎么样?是不是有点心动了?下面就正式开始吧! 

首先在以前的项目QLFUI上新建一个窗体(不是用户控件咯)并重新命名为MainForm。

如图: 

 

 

接着来设置几个属性,如下:

FormBorderStyle:None

Size:300,300

接下来就是用picturebox 和 panel来布局了。这一部分可能比较繁琐。首先总体介绍一下大概的布局。见图:

 

  因为我们做的窗体是圆角的,所以窗体的四个角上(红色)我们会放上四个picturebox。标题栏和状态栏(蓝色)会是两个panel。二个边框线(绿色)是两个picturebox。最后的主体部分(棕色)是一个panel。说完了大体的布局,下面就来动手做啦。一个一个来:

左上角

Name:ptbtl

Location:0,0

Size:10,31

上方的标题栏

Modifiers:Protected;

Name:panelt

Location:10,0

Size:280,31

右上角

Name:ptbtr

Location:290,0

Size:10,31

左侧的边框线

Name:ptbml

Location:0,31

Size:2,232

主窗体部分

Modifiers:Protected;

BackgroundImageLayout:Stretch

Name:panelm

Location:2,31

Size:296,232

右侧的边框线

Name:ptbmr

Location:298,31

Size:2,232

左下角

Name:ptbbl

Location:0,263

Size:10,37

下方的状态栏

Modifiers:Protected;

Name:panelb

Location:10,263

Size:280,37

右下角

Name:ptbbr

Location:290,263

Size:10,37

最后还有右上方的三个按钮可不要忘记了,注意按钮都是在panelt里面设置的而且这三个按钮都是我们前面做的按钮哦,终于派上用场啦!

最小化按钮

Caption:0

Name:btmin

Location:180,0

Size:31,22

最大化按钮

Caption:0

Name:btmax

Location:211,0

Size:31,22

关闭按钮

Caption:0

Name:btclose

Location:243,0

Size:37,22

还原按钮

Caption:0

Name:btres

Location:211,-30

Size:31,22

 

最后再加上一个label作为窗体的标题

Name:lbtitle

Location:6,9

Size:35,12

BackColor:Transparent

 

呼,终于结束了!不知道大家有没有晕乎乎的感觉,反正我是有点儿了,呵呵。最后的布局图应该是这个样子滴:

老规矩,界面的事情弄完了,现在开始编码了。上代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Reflection;

namespace QLFUI
{
    public partial class MainForm : Form
    {

        #region 变量

        bool needMaxbt = true;  //是否需要最大化按钮
        bool needMinbt = true; //是否需要最小化按钮

        //记录原始的用户设置的大小,用于最大化后返回正常的大小
        int orginWidth;
        int orginHeight;

        //记录窗体的位置
        int X;
        int Y;


        //窗体的状态
        enum stat
        {
            Normal, Max, Min
        }
        stat st = stat.Normal;


        #endregion

        #region 属性

        /// <summary>
        /// 隐藏size属性,通过BorderWidth,BorderHeight设置
        /// </summary>
        [Browsable(false)]
        public new SizeF Size
        {
            get { return base.Size; }
        }

        [CategoryAttribute("QLFSkinDll"), Description("设置窗体宽度")]
        public int BorderWidth
        {
            get { return this.Width; }
            set
            {
                if (value < 180)
                {
                    MessageBox.Show("宽度太小了,宽度最少是180个像素。");
                    return;
                }
                if (this.Width != value)
                {
                    int newwidth = value;
                    this.Width = value;

                    ptbml.Left = 0;
                    ptbmr.Left = newwidth - 2;
                    ptbtr.Left = newwidth - 10;
                    ptbbr.Left = newwidth - 10;

                    panelm.Width = newwidth - 4;
                    panelb.Width = newwidth - 20;
                    panelt.Width = newwidth - 20;

                    btclose.Left = newwidth - 57;
                    btmin.Left = newwidth - 120;
                    btmax.Left = newwidth - 89;
                    btres.Left = newwidth - 89;
                }

            }
        }

        [CategoryAttribute("QLFSkinDll"), Description("设置窗体高度")]
        public int BorderHeight
        {
            get { return this.Height; }
            set
            {
                if (this.Height != value)
                {
                    int newheight = value;
                    this.Height = value;

                    panelm.Height = newheight - 68;
                    ptbml.Height = newheight - 68;
                    ptbmr.Height = newheight - 68;
                    ptbml.Top = 31;
                    ptbmr.Top = 31;

                    panelb.Top = newheight - 37;
                    ptbbl.Top = newheight - 37;
                    ptbbr.Top = newheight - 37;
                }

            }
        }

        [CategoryAttribute("QLFSkinDll"), Description("设置窗体标题")]
        public string FormTitle
        {
            get { return lbtitle.Text; }
            set { lbtitle.Text = value; }
        }

        [CategoryAttribute("QLFSkinDll"), Description("是否需要最大化按钮")]
        public bool NeedMax
        {
            get { return needMaxbt; }
            set
            {
                needMaxbt = value;
                if (value == false)
                {
                    this.btmax.Top = -50;
                    this.btmin.Left = this.Width - 89;
                }
                else
                {
                    this.btmax.Top = 0;
                    this.btmin.Left = this.Width - 120;
                }
            }
        }

        [CategoryAttribute("QLFSkinDll"), Description("是否需要最小化按钮")]
        public bool NeedMix
        {
            get { return needMinbt; }
            set
            {
                needMinbt = value;
                if (value == false)
                {
                    this.btmin.Top = -50;
                }
                else
                {
                    this.btmin.Top = 0;
                }
            }
        }

        #endregion

        #region 构造函数

        public MainForm()
        {
            InitializeComponent();

            //左上角
            this.ptbtr.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.bordertr.bmp")));
            Bitmap b = this.ptbtr.BackgroundImage as Bitmap;
            b.MakeTransparent(Color.FromArgb(255, 0, 255));

            //右上角
            this.ptbtl.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.bordertl.bmp")));
            Bitmap b1 = this.ptbtl.BackgroundImage as Bitmap;
            b1.MakeTransparent(Color.FromArgb(255, 0, 255));

            //左下角
            this.ptbbl.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.borderbl.bmp")));
            Bitmap b2 = this.ptbbl.BackgroundImage as Bitmap;
            b2.MakeTransparent(Color.FromArgb(255, 0, 255));

            //右下角
            this.ptbbr.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.borderbr.bmp")));
            Bitmap b3 = this.ptbbr.BackgroundImage as Bitmap;
            b3.MakeTransparent(Color.FromArgb(255, 0, 255));


            //中间左
            this.ptbml.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.borderml.bmp")));
            Bitmap b4 = this.ptbtl.BackgroundImage as Bitmap;
            b4.MakeTransparent(Color.FromArgb(255, 0, 255));

            //中间右
            this.ptbmr.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.bordermr.bmp")));
            Bitmap b5 = this.ptbtl.BackgroundImage as Bitmap;
            b5.MakeTransparent(Color.FromArgb(255, 0, 255));

            this.panelt.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.bordertm.bmp")));
            this.panelm.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.bg.bmp")));
            this.panelb.BackgroundImage = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.borderbm.bmp")));

            Bitmap closeBitmap = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.CloseBtn.bmp")));
            closeBitmap.MakeTransparent(Color.FromArgb(255, 0, 255));
            this.btclose.NormalImage = closeBitmap.Clone(new Rectangle(0, 0, 37, 22), PixelFormat.Format64bppPArgb);
            this.btclose.MoveImage = closeBitmap.Clone(new Rectangle(37, 0, 37, 22), PixelFormat.Format64bppPArgb);
            this.btclose.DownImage = closeBitmap.Clone(new Rectangle(74, 0, 37, 22), PixelFormat.Format64bppPArgb);
            this.btclose.BackgroundImage = this.btclose.NormalImage;

            Bitmap minBitmap = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.MinBtn.bmp")));
            minBitmap.MakeTransparent(Color.FromArgb(255, 0, 255));
            this.btmin.NormalImage = minBitmap.Clone(new Rectangle(0, 0, 31, 22), PixelFormat.Format64bppPArgb);
            this.btmin.MoveImage = minBitmap.Clone(new Rectangle(31, 0, 31, 22), PixelFormat.Format64bppPArgb);
            this.btmin.DownImage = minBitmap.Clone(new Rectangle(62, 0, 31, 22), PixelFormat.Format64bppPArgb);
            this.btmin.BackgroundImage = this.btmin.NormalImage;

            Bitmap maxBitmap = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.border.MaxBtn.bmp")));
            maxBitmap.MakeTransparent(Color.FromArgb(255, 0, 255));
            this.btmax.NormalImage = maxBitmap.Clone(new Rectangle(0, 0, 32, 22), PixelFormat.Format64bppPArgb);
            this.btmax.MoveImage = maxBitmap.Clone(new Rectangle(32, 0, 32, 22), PixelFormat.Format64bppPArgb);
            this.btmax.DownImage = maxBitmap.Clone(new Rectangle(64, 0, 32, 22), PixelFormat.Format64bppPArgb);
            this.btmax.BackgroundImage = this.btmax.NormalImage;

            Bitmap resBitmap = new Bitmap(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"QLFUI.Res.button.RestrBtn.bmp")));
            resBitmap.MakeTransparent(Color.FromArgb(255, 0, 255));
            this.btres.NormalImage = resBitmap.Clone(new Rectangle(0, 0, 32, 22), PixelFormat.Format64bppPArgb);
            this.btres.MoveImage = resBitmap.Clone(new Rectangle(32, 0, 32, 22), PixelFormat.Format64bppPArgb);
            this.btres.DownImage = resBitmap.Clone(new Rectangle(64, 0, 32, 22), PixelFormat.Format64bppPArgb);
            this.btres.BackgroundImage = this.btres.NormalImage;

            Win32.SetWindowLong(this.Handle, -16, Win32.WS_SYSMENU + Win32.WS_MINIMIZEBOX);
        }

        #endregion

        #region 事件

        private void panelt_MouseDown(object sender, MouseEventArgs e)
        {
            if (st != stat.Max)
            {
                if (e.Button == MouseButtons.Left)
                {
                    Win32.ReleaseCapture();
                    Win32.SendMessage(Handle, 274, 61440 + 9, 0);
                }
            }

        }

        protected virtual void btmin_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }

        protected virtual void btclose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        protected virtual void btmax_Click(object sender, EventArgs e)
        {
            this.btmax.Top -= 30;
            this.btres.Top = 0;

            //最大化之前记录窗口信息便于缩小
            orginHeight = this.Height;
            orginWidth = this.Width;
            X = this.Location.X;
            Y = this.Location.Y;

            this.Top = 0;
            this.Left = 0;
            BorderWidth = Screen.PrimaryScreen.WorkingArea.Width;
            BorderHeight = Screen.PrimaryScreen.WorkingArea.Height;

            st = stat.Max;
        }

        private void btres_Click(object sender, EventArgs e)
        {
            this.btmax.Top = 0;
            this.btres.Top -= 30;

            this.Left = X;
            this.Top = Y;
            BorderWidth = orginWidth;
            BorderHeight = orginHeight;
            st = stat.Normal;
        }

        #endregion

        
    }
}

变量部分:

变量我在代码里都注明了其作用。这里只会说说为什么要这些变量。首先needMaxbt和needMinbt是指示该窗体是否需要最大化或最小化按钮的,毕竟窗体有很多种吧,比如说一个消息框就没必要用最大化按钮吧,所以我们这边也就设置的灵活一点,下面会将这两个变量封装成属性以便在设计模式下方便的选择。

接下来的四个变量

orginWidth;

orginHeight;

X;

Y;

都是用来记录窗体的信息的,分别是窗体的宽,高,x座标值,y座标值。为什么要设置一些变量呢?说起来也比较郁闷,这些都是为了窗口的最大化和还原准备的。可能有人会问了最大化不就一句话嘛
this.WindowState= FormWindowState.Maximized;何必还要这些个变量呢。关键郁闷就郁闷在这里啦,假如直接用这一句话最后窗体虽然也能最大化可就是右上角的一小段边框线莫名其妙的没有了,如图:

搞了半天也不明白。最后不得已才想出个自己手动最大化的方法来,讲到具体的地方再和大家说吧。

接下来我又定义了一个枚举stat,用来表示窗体的三种状态,正常,最大化,最小化。下面的那个st就是枚举的一个变量,默认的是normal也就是窗体正常显示啦。

变量讲完了接下来讲属性。

第一个属性Size。这个属性有点特别,因为它里面有一个new关键字,为什么呢?因为Size这个属性在设计器里面原来就有了所以我们加上这个new就相当于重写这个属性啦。这个属性里面我只写了一个get方法。我写这个属性的目的就是要将设计器下原来的size属性隐藏(看见了上面的那个[Browsable(false)]吗?就是用它来隐藏的),这样别人如果要设置窗体的大小就得修改我们下面提到的BorderWidth和BorderHeight了。那为什么要隐藏原来的实现呢,这是因为当我们设置窗体大小的时候窗体上相应的picturebox和panel也需要相应的改变,这里面就需要设置很多的东西。所以我们另外设置属性来完成这些工作。

第二个属性BorderWidth。毫无疑问,这个就是用来设置窗体的宽度的啦。[CategoryAttribute("QLFSkinDll"), Description("设置窗体宽度")]我就不讲了,前面已经提到过了。里面的get也很简单就是返回当前窗口的宽度。重点讲解set。首先我们用if语句限制了用户设置的值不能小于180像素,因为当小于180个像素的时候,界面上的最小化按钮就有将标题都“挤”没了。所以最小180啦。接下来如果用户设置的值和设置前的不一样,那么就开始一下一连串的设置了。我们用newwidth把用户设置的这个值记录下来,下面要用到。然后紧接着就改变窗体的大小。

如果仅仅这样就结束了你会发现panel,picturebox那些控件的位置都没有动,这不是我们想要的。接下来就是设置这些控件的位置啦。一开始我做的时候也被这些设置搞乱了,最后我用的相对位置才搞清楚的。什么叫相对位置,打比方说无论你窗体的大小怎么变化,关闭按钮总是在距窗体右侧57个像素的位置。按照这个一句来做我们也会发现其他的控件都是一样的。于是一个一个的设置,找相对距离。当然我现在已经找好了,大家就不必再麻烦一个一个找一遍来了。至此,BorderWidth属性讲完。

第三个属性BorderHeight。经过上面BorderWidth属性的讲解,我想height属性就不要我再讲解了,都是一样的道理。

第四个属性FormTitle。看意思就应该看出来了吧,这个是用来设置窗体的标题的。这个在前面讲到的button,checkbox等控件里也有差不多的属性,这里也不就不罗嗦了。

第五个属性NeedMax。这个属性是用来指定窗体是否需要最大化按钮的。怎么指定呢?看代码。如果用户设置的是false也就是不需要,那我们就会将btmax按钮向上移动50个像素(this.btmax.Top = -50;)同时btmin按钮顶替最大化按钮的位置(this.btmin.Left = this.Width - 89;),怎么样?是不是很简单就实现了按钮的隐藏呢?

第六个属性NeedMin。用法参见上面的第五个属性。

接下来讲构造函数。

前四句就不要我讲了吧,前面也遇到过就是开启双缓冲和系统默认的是初始化控件。剩下的N多的语句都是用来加载图片的,其实也没啥好说的要说的点前面控件制作的时候也已经介绍过了,呵呵。。。说一下最后一句的意思吧。因为我们的窗体默认是没有状态栏什么的,所以也就决定了当窗体最小化后右击任务栏上的窗体没有菜单弹出来。如果要实现这个功能就需要借助系统API来完成了。不过在c#里面是不可以直接调用API的,需要实现声明一下。在这里就偷懒一下直接将别人封装好的API类(win32.cs)。一句话完成这个功能:

Win32.SetWindowLong(this.Handle, -16, Win32.WS_SYSMENU+ Win32.WS_MINIMIZEBOX);好了,来运行看看吧:

 

发现问题:

大体的模样是出现了。不过也许大家看图看不清楚,图中被我画圈的其实都不是透明的,而是灰色的。接下来,我们将解决或实现以下几个问题:

1.消除图中不透明的地方

2.实现标题栏的拖动

3.实现窗体的最大化,最小化,关闭功能。

解决不透明的问题:

首先为什么会有灰色的出现呢。不难想到这些灰色就是Picturebox的背景色。边角上的图片对粉红色透明了,但却露出了背景的颜色。知道了原因就好了解决了,就是我们需要将这一块的背景色给透明掉。有人说可以讲picturebox的背景色设置为透明的啊。不错,是可以,但整个窗体的背景色呢?picturebox下面不还是有整个被覆盖的窗体的嘛!窗体是不能像用户控件那样背景色设置为透明的,不过却有一个属性TransparencyKey可以间接的为我们解决这个问题。这个属性的作用是这样的,当这个属性设置为某一种颜色后,那该窗体上的所有的这种颜色将都变为透明色。

所以我们只需将TransparencyKey的颜色和窗体的背景色设置成一样,那么背景色不就自动透明了嘛!在这里我们将所有控件的背景色和TransparencyKey都设置成系统里面的Info颜色(这个颜色你可以自己指定,不过不建议是原来的Control色,因为这样后你再添加一个普通的控件,那么这个控件的一部分也会被透明掉)

实现标题栏的拖动

因为我们的窗体是没有状态栏的,所以当我们想移动窗口的时候发现怎么也”移不动”(不是有个笑话的嘛“中国移不动“,呵呵)。要解决这个问题还是需要借助API来完成。那如何知道别人需要移动窗体呢?这里我们就通过MouseDown这个事件来判断。即如果用户在特定的区域内按下了鼠标左键,那么我们就认为用户需要移动客户端了。这个时候我们调用相应的API向系统通知(SendMessage)让系统做出响应。为了效果好一点,我们将状态栏那一行的所有的MouseDown事件都用这个方法。于是现在我们运行一下看看能否移动窗体。

 

很不错嘛!又是一个问题解决了,现在只要将右上方的三个按钮的功能完成那这个窗体就算基本完成了啦。好了,我们继续。。。

    要处理按钮的事件,毫无疑问我们在设计模式中双击对应的按钮,然后处理。这里还要提一下的就是我们需要在这些事件的每个方法钱加一个virtual让这些个方法可以被重写。道理很简单,假如我按关闭按钮的时候不想其直接关闭而是先弹出对话框提醒一下用户怎么办呢?总不能让用这个控件的人再去修改这个皮肤控件吧,最好的办法就是可以让他们重写按钮的click事件,他们想干嘛干嘛,不想有其他的功能只要不重写就是了。

好了开始说最小化功能吧。很简单,一句话搞定:this.WindowState = FormWindowState.Minimized;然后是关闭按钮,还是一句话的事情:this.close();呵呵貌似这两个功能真简单那!原本最大化也不复杂的,可是遇到了前面说到的那个问题,我们就知道想点儿其他的办法啦。看代码:

当用户点击最大化按钮的时候我们先将最大化按钮上移隐藏,然后将还原按钮降下(还记得我们一开始给他设置的位置是-30吗?),然后我们最大化之前就先记录窗体的大小和位置(就是最开始赋值的那四句话)信息便于后面的还原。接下来的四句就是咱们手动来制造一种窗体最大化的状态啦,首先位置肯定在左上角。然后大小应该就是用户电脑屏幕客户区的大小,这个大小我是用Screen.PrimaryScreen.WorkingArea这个函数得到的。状态设置完了我们只要改一下窗体的状态标识st为stat.Max。然后当用户想还原的时候我们只要在btres的click的点击事件中做一下刚才那个动作的反就行了。怎么样,是不是也不难啊!Ok,最后看一下运行效果:

 

发现问题:

终于是做好了。不过假如你也实际跟我动手做出了一个,你或许会发现以下一些问题:

  1.    点击最大化的时候反应好像有点慢,意思就是大概要过个这个半秒钟才会最大化。

  2.  还有一个问题就是从最小化状态下恢复到最大化状态,窗体左上角会有闪烁现象(闪的实在太快了,没办法截图啊,大家可以应用下我下面上传的DLL运行下就知道了)。我们本来就是想美化窗口,但是如果这些个问题一来实在是给人感觉不好,甚至还不如不美化。在下一篇中我将会带领大家解决这个问题。

  3.    我们的窗体位置虽然能移动了,不过大小还不能拖动

不过还是存在着一些问题,包括运行缓慢和闪烁等一些问题。那么下面就让我们一起来解决吧。

先来说说闪烁和缓慢的问题

我认为最主要的还是由于我们设置了transparent所造成的。网上的一些解释是这样的,当窗体显示时我们设置的透明部分在显示的时候会先呈现出黑色然后再在上面显示图片。这样一来控件一多我们很容易就看见了。不管解释的对不对,反正我们知道设置窗体的transparent肯定是对窗体的显示有一些负面影响的(接下来的窗体避免设置transparent也确实证明了这一点)。既然是这个transparent“搞的鬼”,那能不能不用它来实现窗体的透明呢?答案是我还没找到。但是不代表这个问题解决不了。

我们不妨先来回忆一下我们为什么要使用这个transparent。还记得下面这张图吧?

 

我们运用transparent就是为了解决四个角上透明度的问题。现在我们不想使用transparent怎么办呢?其实简单的换个思路就行了。我们为什么非要那边是透明的啊,它不透明就让它不透明呗,我们只要在显示的时候“切掉”它不就行了。也就是说我们自己先要在原有的窗口内画出一个圆矩形出来然后四个直角就自然切掉不要了。那怎么画怎么切?看下面的系统API,

 

 

 

 

第一个是用来画的,而第二个当然就是用来切的啦。MSDN上面已经给我们解释的很清楚了。CreateRoundRectRgn前面四个参数分别是左上角和右下角的座标,我们在画的时候只需要将其起点设置为(1,1)终点设置为(this.width-1,this.height-1)就能得到一个外边距相当于1的矩形啦。如图:

图中的蓝色部分就是我们需要省略的部分。

 

最后两个参数分别是椭圆形的宽高(其实就是我们切的圆角的弧度啦)。这个函数执行成功了就会返回切好的区域的句柄,然后我们只要在SetWindowRgn中拿到这个handle就可以把我们需要的圆矩形部分切下来啦。

可是好像又有一个问题那!到底该什么时候切呢?铅笔,刀都准备好了,但总不能让他傻傻站那儿不知道什么时候动手吧!时机很重要。我们应该让他在窗体大小变化的时候就行动一次,这样才能保证每次窗体大小变化(比如说最大化)后还能保证是圆角矩形。好啦,原理讲好了,现在该真正动手干活咯。

首先第一步就是切掉原来窗体的transparent,这个就不用我多说了吧!会设置还不会取消嘛!接着就重写一个OnResize函数就可以啦。两句话搞定:

protected overridevoid OnResize(EventArgse)
{
    base.OnResize(e);
    intRgn = Win32.CreateRoundRectRgn(1, 1, this.Width - 1, this.Height- 1, 7, 7);
    Win32.SetWindowRgn(this.Handle, Rgn, true);
}

好了,来运行一下看看效果吧!

 

现在我们再来最大化,最小化还原看看效果确实比上次好多了。基本看不到左上角还有闪烁的情况了,而且响应也蛮快的。不过大家也从上图看出来了,就是最大化的时候周围有空隙的啊。其实也不难想到这些空隙就是我们切掉不要的那一部分窗体。要想最大化的时候窗体完全覆盖只要在最大化的时候稍微增加一下窗体的宽和高就是了。于是我们在上一期的代码中再对最大化事件做以下修改。

private voidbtmax_Click(object sender, EventArgs e)
    {
          this.btmax.Top-= 30;
          this.btres.Top= 0;

          //最大化之前记录窗口信息便于缩小
          orginHeight = this.Height;
          orginWidth = this.Width;
          X = this.Location.X;
          Y = this.Location.Y;

          this.Top= 1;
          this.Left= 1;
          BorderWidth = Screen.PrimaryScreen.WorkingArea.Width +4;
          BorderHeight = Screen.PrimaryScreen.WorkingArea.Height +4;
          st = stat.Max;
}

其中这个窗体的宽和高我试了一下,加4正好差不多能覆盖原来的空隙了。大家可以试一下。

OK,闪烁终于算是解决了。接下来实现窗体大小的拖动功能。

这个问题还是由于我们的窗体样式为None导致的(看来这个None还真是搞出了不少麻烦呢)。不过不管怎样,咱还是想办法解决吧。前面的窗口移动我们是通过API来完成的,没有疑问的大小的调整我们也借助系统API发送消息来完成。Win32.SendMessage(Handle,274, 61440 + x, 0);其中的x代表了不同的数字,当数字不同的时候产生的拖动大小的方向也不同。具体的数字代表的方向大家可以看我的代码。

现在最主要的问题就是实现鼠标到达窗体边缘时可以拖动。这里我运用了MouseMove这个事件。当鼠标到达指定区域时我们通过判断鼠标左键是否按下来实现改变大小消息的发送与否。我举一个稍微复杂一点的事件说一下吧。拿右上角来说吧。右上角运用ptbtr_MouseMove这个事件来判断是否更改大小。看图:

 

我将右上角的区域分为了四个区域。当鼠标分别落在1,2,4区域并按下鼠标的时候我们才相应的赋予x值从而改变窗体大小。那么如何判断鼠标经过的区域呢?我们可以直接用MouseEventArgs e参数中的x,y属性。该属性表明了鼠标在相应的panel或者picture下的相对位置。还好是相对位置,不然我们又要麻烦了,呵呵。

看代码:首先我们用if (!sizeAble)判断是否设置了可更改大小。当可以改变大小时,我们再逐一判断鼠标左键按下时鼠标的位置,并相应作出更改。代码中我那个点是采用3个像素的位置作为分界线的(就是1和4的分界区),这个位置如果大家觉得不合适可以自己更改。只要按照上面的思路去一个一个的设置窗体最后肯定会获得你想要的效果的。

好了,窗体部分经过3个部分的讲解终于算是结束了

相信大家对控件的编写也有了一定的“感觉”了吧。这个窗体其实说起来还有一些地方不够完善的,比如说窗体的起始位置没有地方设置,再比如说当窗体上的控件太多的时候加载显示窗体还是有一定程度上的不好的显示现象的等等。这些缺点还只是我暂时发现的,肯定还有其他一些不好的地方就等待着细心而认真的你发现并去尝试着解决它了。

 

原文:http://www.blue1000.com/bkhtml/2010-12/69873p6.htm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章