C# 自定义窗体收缩容器 伸缩控件的实现

Posted on 2020-03-11 22:40  轻吟浅唱,蓦然花开  阅读(2076)  评论(2编辑  收藏  举报

直接上效果

 

想要实现的效果,不用解释也看得出来了,为了给窗体节省空间,让它可以贴附在窗体的边缘。

那么怎么实现这个效果呢?原理在于对Padding这个属性的妙用。

另外可以看见,窗体在设计的时候也是可以进行事件的交互的,就像TabControl在设计的时候可以点击每一个Page一样,关于这个如果有兴趣,就可以参考一下msdn 关于ParentControlDesigner和ControlDesigner给的例子了。

好了废话不多说先上容器的代码

  1 [Designer(typeof(MiracleControls.ControlDesigner.LayoutPanelDesigner))]
  2     [ToolboxBitmap(typeof(LayoutPanel))]
  3     public class LayoutPanel : Panel
  4     {
  5         private System.ComponentModel.Container components = null;
  6         public LayoutPanel()
  7         {
  8             components = new System.ComponentModel.Container();
  9             DoubleBuffered = true;
 10             BackColor = Color.Transparent;
 11             Padding = new Padding(0, 0, _controlSize, 0);
 12             if (_state == States.Open)
 13                 buttonRect = new Rectangle(Width - _controlSize, Height / 2 - _controlSize / 2, _controlSize, _controlSize);
 14             else
 15                 buttonRect = new Rectangle(0, Height / 2 - _controlSize / 2, _controlSize, _controlSize);
 16             Dock = DockStyle.Right;
 17         }
 18         #region Properties
 19         private Color _arrowColor = Color.White;
 20         private int _controlSize = 20;
 21         private Color _buttonColor = Color.FromArgb(55, 55, 55);
 22         private Color _hoverColor = Color.Orange;
 23         private States _state = States.Open;
 24         private int _memorySize = 50;
 25         private bool _hover = false;
 26         private bool _down = false;
 27 
 28         public override DockStyle Dock 
 29         {
 30             get { return base.Dock; }
 31             set { base.Dock = value == DockStyle.Left || value == DockStyle.Right ? value : Dock; }
 32         }
 33         public int ButtonSize
 34         {
 35             get { return _controlSize; }
 36             set { _controlSize = value >= 20 ? value : 20; changeSize(); Invalidate(); }
 37         }
 38         public Color ArrowColor
 39         {
 40             get { return _arrowColor; }
 41             set { _arrowColor = value; Invalidate(); }
 42         }
 43         public Color ButtonColor
 44         {
 45             get { return _buttonColor; }
 46             set { _buttonColor = value; Invalidate(); }
 47         }
 48         public Color HoverColor
 49         {
 50             get { return _hoverColor; }
 51             set { _hoverColor = value;Invalidate(buttonRect); }
 52         }
 53         private bool Hover
 54         {
 55             get { return _hover; }
 56             set
 57             {
 58                 _hover = value;
 59                 if (_hover)
 60                     Cursor = Cursors.Hand;
 61                 else
 62                     Cursor = Cursors.Default;
 63                 Invalidate(buttonRect);
 64             }
 65         }
 66         private bool Down
 67         {
 68             get { return _down; }
 69             set { _down = value; }
 70         }
 71         [Browsable(false)]
 72         public int MemorySize
 73         {
 74             get { return _memorySize; }
 75             set { _memorySize = value; }
 76         }
 77         public States State
 78         {
 79             get { return _state; }
 80             set
 81             {
 82                 _state = value;
 83                 changeSize();
 84                 Invalidate(buttonRect);
 85             }
 86         }
 87         public enum States
 88         { Open, Close };
 89         #endregion
 90 
 91         private void changeSize()
 92         {
 93             if (State == States.Close)
 94             {
 95                 if (Dock == DockStyle.Left)
 96                 {
 97                     Size = new Size(_controlSize, Height);
 98                     Padding = new Padding(_controlSize, 0, 0, 0);
 99                 }
100                 else if (Dock == DockStyle.Right)
101                 {
102                     Size = new Size(_controlSize, Height);
103                     Padding = new Padding(_controlSize, 0, 0, 0);
104                 }
105             }
106             else
107             {
108                 if (Dock == DockStyle.Left)
109                 {
110                     Padding = new Padding(0, 0, _controlSize, 0);
111                     Size = new Size(_memorySize, Height);
112                 }
113                 else if (Dock == DockStyle.Right)
114                 {
115                     Size = new Size(MemorySize, Height);
116                     Padding = new Padding(_controlSize, 0, 0, 0);
117                 }
118             }
119         }
120         Rectangle buttonRect;
121         protected override void OnPaint(PaintEventArgs e)
122         {
123             base.OnPaint(e);
124             Graphics G = e.Graphics;
125             G.SmoothingMode = SmoothingMode.HighQuality;
126             //绘制虚线框
127             if (Site != null)
128                 using (Pen pen = new Pen(Color.Blue))
129                 {
130                     pen.DashStyle = DashStyle.Custom;
131                     pen.DashPattern = new float[] { 5, 5 };
132                     G.DrawRectangle(pen, new Rectangle(Padding.Left, Top, Width - Padding.Right - 1, Height - Padding.Bottom - 1));
133                 }
134 
135             Rectangle circleRect = new Rectangle(buttonRect.X + 1, buttonRect.Y + 1, buttonRect.Width - 2, buttonRect.Height - 2);
136             using (LinearGradientBrush lb = new LinearGradientBrush(Dock == DockStyle.Left ? buttonRect :
137                 new Rectangle(buttonRect.X, buttonRect.Y, buttonRect.Width + 1, buttonRect.Height), BackColor, Color.FromArgb(180, _buttonColor), 0F))
138             {
139                 lb.SetBlendTriangularShape(0.5F);
140                 using (GraphicsPath GP = CreatePath())
141                     G.FillPath(lb, GP);
142             }
143             using (SolidBrush sb = new SolidBrush(ControlPaint.Dark(_buttonColor, 0.2F)))
144                 G.FillEllipse(sb, buttonRect);
145             using (SolidBrush sb = new SolidBrush(_buttonColor))
146             {
147                 G.FillEllipse(sb, circleRect);
148             }
149             if (Dock == DockStyle.Left || Dock == DockStyle.Right)
150                 DrawLeftRight(G, circleRect);
151         }
152         private void DrawLeftRight(Graphics G, Rectangle circleRect)
153         {
154             using (Pen pen = new Pen(_hover ? _hoverColor : _arrowColor, 2))
155             {
156                 if (Dock == DockStyle.Left)
157                     if (State == States.Open)
158                     {
159                         G.DrawLine(pen, circleRect.X + circleRect.Width / 8 + circleRect.Width / 4, circleRect.Y + 1 + circleRect.Height / 2,
160                                circleRect.X + circleRect.Width / 8 + circleRect.Width / 2, circleRect.Y + 1 + circleRect.Height / 4);
161                         G.DrawLine(pen, circleRect.X + circleRect.Width / 8 + circleRect.Width / 4, circleRect.Y + circleRect.Height / 2,
162                             circleRect.X + circleRect.Width / 8 + circleRect.Width / 2, circleRect.Y + circleRect.Height - circleRect.Height / 4);
163                     }
164                     else
165                     {
166                         G.DrawLine(pen, circleRect.X + circleRect.Width / 2 - circleRect.Width / 8, circleRect.Y + 1 + circleRect.Height / 4,
167                               circleRect.X - circleRect.Width / 8 + circleRect.Width - circleRect.Width / 4, circleRect.Y + 1 + circleRect.Height / 2);
168                         G.DrawLine(pen, circleRect.X + circleRect.Width / 2 - circleRect.Width / 8, circleRect.Y + circleRect.Height - circleRect.Height / 4,
169                                circleRect.X - circleRect.Width / 8 + circleRect.Width - circleRect.Width / 4, circleRect.Y + circleRect.Height / 2);
170                     }
171                 else
172                 if (State == States.Close)
173                 {
174                     G.DrawLine(pen, circleRect.X + circleRect.Width / 8 + circleRect.Width / 4, circleRect.Y + 1 + circleRect.Height / 2,
175                            circleRect.X + circleRect.Width / 8 + circleRect.Width / 2, circleRect.Y + 1 + circleRect.Height / 4);
176                     G.DrawLine(pen, circleRect.X + circleRect.Width / 8 + circleRect.Width / 4, circleRect.Y + circleRect.Height / 2,
177                         circleRect.X + circleRect.Width / 8 + circleRect.Width / 2, circleRect.Y + circleRect.Height - circleRect.Height / 4);
178                 }
179                 else
180                 {
181                     G.DrawLine(pen, circleRect.X + circleRect.Width / 2 - circleRect.Width / 8, circleRect.Y + 1 + circleRect.Height / 4,
182                           circleRect.X - circleRect.Width / 8 + circleRect.Width - circleRect.Width / 4, circleRect.Y + 1 + circleRect.Height / 2);
183                     G.DrawLine(pen, circleRect.X + circleRect.Width / 2 - circleRect.Width / 8, circleRect.Y + circleRect.Height - circleRect.Height / 4,
184                            circleRect.X - circleRect.Width / 8 + circleRect.Width - circleRect.Width / 4, circleRect.Y + circleRect.Height / 2);
185                 }
186             }
187         }
188         private GraphicsPath CreatePath()
189         {
190             GraphicsPath GP = new GraphicsPath();
191             if (Dock == DockStyle.Left)
192                 GP.AddLines(new PointF[] {new PointF(Width-_controlSize,Height/2-2*_controlSize),
193             new Point(Width-_controlSize/2,Height/2-_controlSize),new PointF(Width-_controlSize/2,Height/2+_controlSize)
194             ,new PointF(Width-_controlSize,Height/2+2*_controlSize),new PointF(Width-_controlSize,Height/2-2*_controlSize)});
195             else if (Dock == DockStyle.Right)
196                 GP.AddLines(new PointF[] {new PointF(_controlSize,Height/2-2*_controlSize),
197             new Point(_controlSize/2,Height/2-_controlSize),new PointF(_controlSize/2,Height/2+_controlSize)
198             ,new PointF(_controlSize,Height/2+2*_controlSize),new PointF(_controlSize,Height/2-2*_controlSize)});
199             return GP;
200         }
201         protected override void OnMouseDown(MouseEventArgs e)
202         {
203             base.OnMouseDown(e);
204             if (e.X > buttonRect.X && e.X < buttonRect.X + buttonRect.Width && e.Y > buttonRect.Y && e.Y < buttonRect.Y + buttonRect.Height)
205             {
206                 if (!Down)
207                     Down = true;
208             }
209             else { if (Down) Down = false; }
210         }
211         protected override void OnMouseLeave(EventArgs e)
212         {
213             base.OnMouseLeave(e);
214             Hover = false;
215         }
216         protected override void OnMouseUp(MouseEventArgs e)
217         {
218             base.OnMouseUp(e);
219             Down = false;
220         }
221         protected override void OnMouseMove(MouseEventArgs e)
222         {
223             base.OnMouseMove(e);
224             if (e.X > buttonRect.X && e.X < buttonRect.X + buttonRect.Width && e.Y > buttonRect.Y && e.Y < buttonRect.Y + buttonRect.Height)
225             {
226                 if (!Hover)
227                     Hover = true;
228             }
229             else { if (Hover) Hover = false; }
230         }
231         protected override void OnMouseClick(MouseEventArgs e)
232         {
233             base.OnMouseClick(e);
234             if (Hover && Down)
235                 State = _state == States.Open ? States.Close : States.Open;
236         }
237         protected override void OnResize(EventArgs eventargs)
238         {
239             base.OnResize(eventargs);
240             if (State != States.Close && this.Width > _controlSize)
241                 MemorySize = Width;
242             if (Dock == DockStyle.Left)
243             {
244                 if (_state == States.Open)
245                     buttonRect = new Rectangle(Width - _controlSize, Height / 2 - _controlSize / 2, _controlSize - 1, _controlSize - 1);
246                 else
247                     buttonRect = new Rectangle(0, Height / 2 - _controlSize / 2, _controlSize - 1, _controlSize - 1);
248             }
249             else if (Dock == DockStyle.Right)
250             {
251                 if (_state == States.Open)
252                     buttonRect = new Rectangle(0, Height / 2 - _controlSize / 2, _controlSize - 1, _controlSize - 1);
253                 else
254                     buttonRect = new Rectangle(0, Height / 2 - _controlSize / 2, _controlSize - 1, _controlSize - 1);
255             }
256             Invalidate();
257         }
258         protected override void Dispose(bool disposing)
259         {
260             if (disposing)
261             {
262                 if (components != null)
263                     components.Dispose();
264             }
265             base.Dispose(disposing);
266         }
267     }
View Code

首先 映入眼帘的有这样一行特性[Designer(typeof(MiracleControls.ControlDesigner.LayoutPanelDesigner))]这是什么意思呢,这就是让这个容器在窗体设计器上可以进行交互的一个必不可少的东西了,至于MiracleControls.ControlDesigner这个命名空间,可以不用关心,因为这是写在我自己的类库中的,有点懒,代码中出现类似的命名空间,均不用关心。

好了,先提供设计器的类:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.ComponentModel;
 6 using System.Drawing;
 7 using MiracleControls.ContainerControls;
 8 using System.Windows.Forms;
 9 using System.Windows.Forms.Design;
10 
11 namespace MiracleControls.ControlDesigner
12 {
13     public class LayoutPanelDesigner : System.Windows.Forms.Design.ParentControlDesigner
14     {
15         public override SelectionRules SelectionRules
16         {
17             get
18             {
19                 if ((Control as LayoutPanel).State == LayoutPanel.States.Close)
20                     return System.Windows.Forms.Design.SelectionRules.None;//让容器在关闭的状态下不可以被调整
21                 else
22                     return SelectionRules.AllSizeable;
23             }
24         }
25         protected override bool GetHitTest(Point point)
26         {
27             LayoutPanel lay = base.Control as LayoutPanel;//得到宿主控件
28             Point e = lay.PointToClient(point);//得到鼠标的坐标
29             Rectangle buttonRect = new Rectangle(lay.Width - lay.ButtonSize, lay.Height / 2 - lay.ButtonSize / 2, lay.ButtonSize - 1, lay.ButtonSize - 1);//定义可以点击的按钮区域
30 if (lay.Dock == DockStyle.Left) 31 { 32 if (lay.State == LayoutPanel.States.Open) 33 buttonRect = new Rectangle(lay.Width - lay.ButtonSize, lay.Height / 2 - lay.ButtonSize / 2, lay.ButtonSize - 1, lay.ButtonSize - 1); 34 else 35 buttonRect = new Rectangle(0, lay.Height / 2 - lay.ButtonSize / 2, lay.ButtonSize - 1, lay.ButtonSize - 1); 36 } 37 else if (lay.Dock == DockStyle.Right) 38 { 39 if (lay.State == LayoutPanel.States.Open) 40 buttonRect = new Rectangle(0, lay.Height / 2 - lay.ButtonSize / 2, lay.ButtonSize - 1, lay.ButtonSize - 1); 41 else 42 buttonRect = new Rectangle(0, lay.Height / 2 - lay.ButtonSize / 2, lay.ButtonSize - 1, lay.ButtonSize - 1); 43 } 44 if (e.X > buttonRect.X && e.X < buttonRect.X + buttonRect.Width && e.Y > buttonRect.Y && e.Y < buttonRect.Y + buttonRect.Height) 45 { 46 if (isMouseDown) 47 { 48 lay.State = lay.State == LayoutPanel.States.Close ? LayoutPanel.States.Open : LayoutPanel.States.Close; 49 isMouseDown = false; 50 } 51 } 52 else 53 { isMouseDown = false; } 54 return false; 55 } 56 bool isMouseDown = false; 57 58 protected override void WndProc(ref Message m) 59 { 60 if (m.Msg == 0x0202) 61 { isMouseDown = true; } 62 base.WndProc(ref m); 63 } 64 } 65 }

代码中没什么解释,这也是我一个不好的习惯。恐怕有一天自己都看不懂了。

 先看图

 

 这是一个 继承Panel的容器,这里为了空出位置 绘制可以点击的那个按钮,所以我们把这个容器的Padding改了,定义按钮的尺寸为ButtonSize 那么上面的情况,按钮的的位置也就好计算了,自己去悟吧。

 

 收缩的效果如上图,只显示一个可以展开的按钮,收缩的原理猜也能够猜测到了,就是改变了容器的尺寸。假如我们把容器的尺寸改变成ButtonSize 并且这个容器的Dock属性 改为Right 那么是不是就可以贴附在窗体的右侧了呢?想想这个道理就很简单对不对。那么此时我们有一个问题,就是收缩之后怎么恢复呢?在代码1中可以看到我写的,我用一个属性专门记录了它展开时候的宽度,如果重新展开那么,恢复尺寸即可。

 

 那么上图的这个 点击事件 我们怎么判断呢,这很简单,判断鼠标按下和移动的位置,就可以得到用户是否点击的这里了。提醒一下哦,鼠标移动和按下必须是同一个区域。至于按钮的形状,重写OnPaint就可以了。