【WinForm】自定義控件(進度控制條)


上篇說了如何創建自定義控件,接下來說說如何自定義屬性,如何繪制控件,以進度控制條為例,先上效果圖

這里只實現了簡單的進度控制功能,該控件由三部分組成,總長度(底部白色矩形),已加載長度(灰色矩形),控制塊(黑色矩形),百分比

1、首先創建一個類庫,命名為MySlider, 繼承自 Control 類

    public class MySlider : Control
    {
        public MySlider()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
        }
    }

  在構造函數中設置控件Style,ControlStyles枚舉可以參考

http://technet.microsoft.com/zh-cn/subscriptions/system.windows.forms.controlstyles.aspx

2、接下來,我們需要定義以下變量,並對一些變量進行一些默認設置

        Rectangle foreRect;
        Rectangle backRect;
        Rectangle setRect;

        Color backgroundColor = Color.White;
        Color foregroundColor = Color.Gray;
        Color setRectColor = Color.Black;
        Color fontColor = Color.Black;

        int maximum = 100;
        int minimum = 0;
        double myValue = 0;

        bool showPercent;
        float fontSize = 9;
        FontFamily myFontFamily = new FontFamily("宋體");

3、再來,設置屬性值

View Code
        [Category("General"), Description("Show Percent Tag"), Browsable(true)]
        public bool ShowPercentTag
        {
            get { return showPercent; }
            set
            {
                showPercent = value;
                Invalidate();
            }
        }
        [Category("General"), Description("Control's Maximum"), Browsable(true)]
        public int Maximum
        {
            get { return maximum; }
            set
            {
                maximum = value;
                Invalidate();
            }
        }
        [Category("General"), Description("Control's Minimum"), Browsable(true)]
        public int Minimum
        {
            get { return minimum; }
            set
            {
                minimum = value;
                Invalidate();
            }
        }
                
        [Category("General"), Description("Control's FontSize"), Browsable(true)]
        public float FontSize
        {
            get { return fontSize; }
            set
            {
                this.fontSize = value;
                Invalidate();
            }
        }
        [Category("General"), Description("Control's FontFamily"), Browsable(true)]
        public FontFamily MyFontFamily
        {
            get { return myFontFamily; }
            set
            {
                this.myFontFamily = value;
                Invalidate();
            }
        }
        
        [Category("Color"), Browsable(true)]
        public Color BackgroundColor
        {
            get { return backgroundColor; }
            set
            {
                this.backgroundColor = value;
                Invalidate();
            }
        }
        [Category("Color"), Browsable(true)]
        public Color ForegroundColor
        {
            get { return foregroundColor; }
            set
            {
                this.foregroundColor = value;
                Invalidate();
            }
        }
        [Category("Color"), Browsable(true)]
        public Color SetRectColor
        {
            get { return setRectColor; }
            set
            {
                this.setRectColor = value;
                Invalidate();
            }
        }
        [Category("Color"), Browsable(true)]
        public Color FontColor
        {
            get { return fontColor; }
            set
            {
                this.fontColor = value;
                Invalidate();
            }
        }

還有一個myValue沒有設置,后面會講到

4、接下來要確定控件的位置,我們根據 Width 和 Height 屬性來確定矩形的位置,由於Control 類也有這兩個屬性,我們在前面加上new覆蓋掉原有的屬性

        [Category("General"), Description("Control's Width"), Browsable(true)]
        public new int Width
        {
            get { return base.Width; }
            set
            {
                base.Width = value;
                foreRect.X = backRect.X = base.Width / 20;
                backRect.Width = base.Width * 9 / 10;
                foreRect.Width = (int)(myValue / maximum * backRect.Width);
                setRect.X = (int)(myValue / maximum * (backRect.Width - backRect.Height) + foreRect.X);

                Invalidate();
            }
        }
        [Category("General"), Description("Control's Height"), Browsable(true)]
        public new int Height
        {
            get { return base.Height; }
            set
            {
                base.Height = value;
                foreRect.Height = backRect.Height = setRect.Height = setRect.Width = base.Height / 3;
                foreRect.Y = backRect.Y = setRect.Y = base.Height / 3;
                Invalidate();
            }
        }

通過Width 和 Height 屬性,計算出三個矩形的位置和大小,接下來是Value屬性

5、在進度控制條中,這個Value屬性比較重要,我們經常需要相應這個Value值變化的事件,例如,播放音樂的時候,用戶拖動進度條改變Value時,需要使音樂播放到相應的位置

  所以在這里我們先定義一個事件,當外部為該事件添加了響應函數時,事件就會生效,否則為OnValueChanged的值為null

        protected EventHandler OnValueChanged;
        public event EventHandler ValueChanged
        {
            add
            {
                if (OnValueChanged != null)
                {
                    foreach (Delegate d in OnValueChanged.GetInvocationList())
                    {
                        if (object.ReferenceEquals(d, value)) { return; }
                    }
                }
                OnValueChanged = (EventHandler)Delegate.Combine(OnValueChanged, value);
            }
            remove
            {
                OnValueChanged = (EventHandler)Delegate.Remove(OnValueChanged, value);
            }
        }

6、接下來定義Value屬性

  當Value值改變的時候,重新設置矩形的進度,控制塊的位置,並且重繪控件

        [Category("General"), Description("Control's Value"), Browsable(true)]
        public double Value
        {
            get { return myValue; }
            set
            {
                if (myValue < Minimum)
                    throw new ArgumentException("小於最小值");
                if (myValue > Maximum)
                    throw new ArgumentException("超過最大值");
                
                myValue = value;
                foreRect.Width = (int)(myValue / maximum * backRect.Width);
                setRect.X = (int)(myValue / maximum * (backRect.Width - backRect.Height) + backRect.X);

                if ((myValue - maximum) > 0)
                {
                    foreRect.Width = backRect.Width;
                    setRect.X = backRect.Width - backRect.Height + backRect.X;
                }

                //如果添加了響應函數,則執行該函數
                if (OnValueChanged != null)
                {
                    OnValueChanged(this, EventArgs.Empty);
                }
                
                Invalidate();
            }
        }

  這樣,屬性就算添加完了,有一個地方需要注意的,Value屬性內如果對進度條的值進行修改,使用myValue變量,而在其他地方,則用Value屬性

7、接下來是繪制控件,從過重載OnPaint方法對控件進行繪制

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            DrawRect(e.Graphics);
            DrawText(e.Graphics);
        }        
        private void DrawRect(Graphics e)
        {
            Pen pen = new Pen(this.foregroundColor);

            e.FillRectangle(new SolidBrush(this.backgroundColor), backRect);
            e.DrawRectangle(new Pen(Color.Black), backRect);

            e.FillRectangle(new SolidBrush(this.foregroundColor), foreRect);
            e.DrawRectangle(new Pen(Color.Black), foreRect);

            e.FillRectangle(new SolidBrush(this.setRectColor), setRect);
            e.DrawRectangle(new Pen(Color.Black), setRect);
        }
        private void DrawText(Graphics e)
        {
            Point point = new Point();
            point.X = this.backRect.X + this.backRect.Width * 3 / 7;
            point.Y = this.backRect.Y + this.backRect.Height / 3;

            SolidBrush brush = new SolidBrush(fontColor);
            Font font = new Font(myFontFamily, this.fontSize);
            string percent = ((int)this.myValue).ToString() + "%";

            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;

            e.DrawString(percent, font, brush, backRect, format);
        }

使用Graphics進行繪制,這里有個要點,通過設置StringFormat可以讓文字居中顯示

 

8、最后還有一個方法OnResize,在設計時,修改控件的大小會調用這個方法,比如:拖動邊緣的箭頭改變控件的大小時,控件也要做相應的改變時,就可以重載該方法,如果沒有重載,就只有在修改完成后才更新控件,不懂的可以自己試一下 : )

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            this.Width = Width;
            this.Height = Height;
            Invalidate();
        }

  好了,控件算是繪制完成了

 

對了,還有,控件繪制完了,卻不能操作,還要對控件進行操作

9、這里通過三個鼠標事件函數,讓鼠標可以拖動控制條

  在構造函數中添加下面語句

        this.MouseDown += MySlider_MouseDown; this.MouseMove += MySlider_MouseMove; this.MouseUp += MySlider_MouseUp;

  添加三個輔助變量,添加響應函數

 Point originPoint; Point originsetRectPoint; bool setRectDown = false; void MySlider_MouseUp(object sender, MouseEventArgs e) { setRectDown = false; } void MySlider_MouseMove(object sender, MouseEventArgs e) { if (setRectDown) { int dd = e.Location.X - originPoint.X; double percent = (double)(originsetRectPoint.X + dd - this.backRect.X) / (this.backRect.Width - this.backRect.Height); if (percent < 0) { this.Value = minimum; this.foreRect.Width = 0; this.setRect.X = backRect.X; } else if (percent > 1) { this.Value = maximum; this.foreRect.Width = this.backRect.Width; this.setRect.X = backRect.X + backRect.Width - backRect.Height; } else { this.Value = percent * maximum; this.foreRect.Width = (int)(percent * this.backRect.Width); this.setRect.X = originsetRectPoint.X + dd; } Invalidate(); } } void MySlider_MouseDown(object sender, MouseEventArgs e) { if (setRect.Contains(e.Location)) { this.originPoint = e.Location; originsetRectPoint = this.setRect.Location; this.setRectDown = true; } }

  到這里,一個自定義的進度控制條算是完成了,功能比較簡單,大家可以自己完善


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM