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

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