這個容器的用途我就不多BB了,直接上效果。小哥哥我的原創,在這里分享給大家。

想要實現的效果,不用解釋也看得出來了,為了給窗體節省空間,讓它可以貼附在窗體的邊緣。
那么怎么實現這個效果呢?原理在於對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 }
首先 映入眼簾的有這樣一行特性[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,就可以在里面盡情得繪制啦,如果對於繪制這方面不懂得畫,就去百度吧,我也懶得講。GDI+ 是很方便的。
算了 晚上10點40分了,該睡覺了,有問題的,再聯系我吧。對了感謝一下那些曾將幫助過我的人們,謝謝。晚安!
