设计起因:最近在做winfrom自定义打印工具,其中项目中需要为打印界面分四个区 于是想到了splitcontainer,由于是在tabcontrol中放入splitcontainer,所以做成自定义splitcontainer是没毛病的
1.先介绍自定义单个SplitContainer放大缩小:
在自己的winfrom项目下新建一个组件类,名为SplitContainerEx.cs
然后写上如下代码,具体可以自己看
1 public partial class SplitContainerEx : SplitContainer 2 { 3 enum MouseState 4 { 5 /// <summary> 6 /// 正常 7 /// </summary> 8 Normal, 9 /// <summary> 10 /// 鼠标移入 11 /// </summary> 12 Hover 13 } 14 15 public SplitContainerEx() 16 { 17 this.SetStyle( 18 ControlStyles.UserPaint | 19 ControlStyles.AllPaintingInWmPaint | 20 ControlStyles.OptimizedDoubleBuffer, true); 21 this.SplitterWidth = 11; 22 this.Panel1MinSize = 0; 23 this.Panel2MinSize = 0; 24 this.Orientation = Orientation.Horizontal; 25 this.SplitterDistance = 50; 26 } 27 28 [Browsable(false)] 29 [EditorBrowsable(EditorBrowsableState.Never)] 30 public new int SplitterWidth 31 { 32 get 33 { 34 return base.SplitterWidth; 35 } 36 set 37 { 38 base.SplitterWidth = 11; 39 } 40 } 41 42 [Browsable(false)] 43 [EditorBrowsable(EditorBrowsableState.Never)] 44 public new int Panel1MinSize 45 { 46 get 47 { 48 return base.Panel1MinSize; 49 } 50 set 51 { 52 base.Panel1MinSize = 0; 53 } 54 } 55 56 [Browsable(false)] 57 [EditorBrowsable(EditorBrowsableState.Never)] 58 public new int Panel2MinSize 59 { 60 get 61 { 62 return base.Panel2MinSize; 63 } 64 set 65 { 66 base.Panel2MinSize = 0; 67 } 68 } 69 70 public enum SplitterPanelEnum 71 { 72 Panel1, 73 Panel2 74 } 75 76 SplitterPanelEnum mCollpasePanel = SplitterPanelEnum.Panel1; 77 /// <summary> 78 /// 进行折叠或展开的SplitterPanel 79 /// </summary> 80 [DefaultValue(SplitterPanelEnum.Panel2)] 81 public SplitterPanelEnum CollpasePanel 82 { 83 get 84 { 85 return mCollpasePanel; 86 } 87 set 88 { 89 if (value != mCollpasePanel) 90 { 91 mCollpasePanel = value; 92 this.Invalidate(this.ControlRect); 93 } 94 } 95 } 96 97 bool mCollpased = false; 98 /// <summary> 99 /// 是否为折叠状态 100 /// </summary> 101 public bool IsCollpased 102 { 103 get { return mCollpased; } 104 } 105 106 Rectangle mRect = new Rectangle(); 107 /// <summary> 108 /// 控制器绘制区域 109 /// </summary> 110 private Rectangle ControlRect 111 { 112 get 113 { 114 if (this.Orientation == Orientation.Horizontal) 115 { 116 mRect.X = this.Width <= 80 ? 0 : this.Width / 2 - 40; 117 mRect.Y = this.SplitterDistance; 118 mRect.Width = 80; 119 mRect.Height = 11; 120 } 121 else 122 { 123 mRect.X = this.SplitterDistance; 124 mRect.Y = this.Height <= 80 ? 0 : this.Height / 2 - 40; 125 mRect.Width = 11; 126 mRect.Height = 80; 127 } 128 return mRect; 129 } 130 } 131 132 133 private Rectangle ControlRect1 134 { 135 get 136 { 137 mRect.X = 0; 138 mRect.Y = this.SplitterDistance; 139 mRect.Width = 80; 140 mRect.Height = 11; 141 return mRect; 142 } 143 } 144 145 /// <summary> 146 /// 鼠标状态 147 /// </summary> 148 MouseState mMouseState = MouseState.Normal; 149 150 protected override void OnPaint(PaintEventArgs e) 151 { 152 base.OnPaint(e); 153 //绘制参数 154 bool collpase = false; 155 if ((this.CollpasePanel == SplitterPanelEnum.Panel1 && mCollpased == false) 156 || this.CollpasePanel == SplitterPanelEnum.Panel2 && mCollpased) 157 { 158 collpase = true; 159 } 160 Color color = mMouseState == MouseState.Normal ? SystemColors.ButtonShadow : SystemColors.ControlDarkDark; 161 //需要绘制的图片 162 Bitmap bmp = CreateControlImage(collpase, color); 163 //绘制区域 164 if (this.Orientation == Orientation.Vertical) 165 { 166 bmp.RotateFlip(RotateFlipType.Rotate90FlipX); 167 } 168 //清除绘制区域 169 e.Graphics.SetClip(this.SplitterRectangle); //这里需要注意一点就是需要清除拆分器整个区域,如果仅清除控制按钮区域,则会出现虚线状态 170 e.Graphics.Clear(this.BackColor); 171 //绘制 172 e.Graphics.DrawImage(bmp, this.ControlRect); 173 174 e.Graphics.DrawString("text", new System.Drawing.Font("宋体", 9), new SolidBrush(Color.Black), this.ControlRect1); 175 } 176 177 public new bool IsSplitterFixed 178 { 179 get 180 { 181 return base.IsSplitterFixed; 182 } 183 set 184 { 185 base.IsSplitterFixed = value; 186 //此处设计防止运行时更改base.IsSplitterFixed属性时导致mIsSplitterFixed变量判断失效 187 if (value && mIsSplitterFixed == false) 188 { 189 mIsSplitterFixed = true; 190 } 191 } 192 } 193 194 bool mIsSplitterFixed = true; 195 protected override void OnMouseMove(MouseEventArgs e) 196 { 197 //鼠标在控制按钮区域 198 if (this.SplitterRectangle.Contains(e.Location)) 199 { 200 if (this.ControlRect.Contains(e.Location)) 201 { 202 //如果拆分器可移动,则鼠标在控制按钮范围内时临时关闭拆分器 203 if (this.IsSplitterFixed == false) 204 { 205 this.IsSplitterFixed = true; 206 mIsSplitterFixed = false; 207 } 208 this.Cursor = Cursors.Hand; 209 mMouseState = MouseState.Hover; 210 this.Invalidate(this.ControlRect); 211 } 212 else 213 { 214 //如果拆分器为临时关闭,则开启拆分器 215 if (mIsSplitterFixed == false) 216 { 217 this.IsSplitterFixed = false; 218 if (this.Orientation == Orientation.Horizontal) 219 { 220 this.Cursor = Cursors.HSplit; 221 } 222 else 223 { 224 this.Cursor = Cursors.VSplit; 225 } 226 } 227 else 228 { 229 this.Cursor = Cursors.Default; 230 } 231 mMouseState = MouseState.Normal; 232 this.Invalidate(this.ControlRect); 233 } 234 } 235 base.OnMouseMove(e); 236 } 237 238 protected override void OnMouseLeave(EventArgs e) 239 { 240 this.Cursor = Cursors.Default; 241 mMouseState = MouseState.Normal; 242 this.Invalidate(this.ControlRect); 243 base.OnMouseLeave(e); 244 } 245 246 protected override void OnMouseClick(MouseEventArgs e) 247 { 248 if (this.ControlRect.Contains(e.Location)) 249 { 250 CollpaseOrExpand(); 251 } 252 base.OnMouseClick(e); 253 } 254 255 int _HeightOrWidth; 256 /// <summary> 257 /// 折叠或展开 258 /// </summary> 259 public void CollpaseOrExpand() 260 { 261 if (mCollpased) 262 { 263 mCollpased = false; 264 this.SplitterDistance = _HeightOrWidth; 265 } 266 else 267 { 268 mCollpased = true; 269 _HeightOrWidth = this.SplitterDistance; 270 if (CollpasePanel == SplitterPanelEnum.Panel1) 271 { 272 this.SplitterDistance = 0; 273 } 274 else 275 { 276 if (this.Orientation == Orientation.Horizontal) 277 { 278 this.SplitterDistance = this.Height - 9; 279 } 280 else 281 { 282 this.SplitterDistance = this.Width - 9; 283 } 284 } 285 } 286 this.Invalidate(this.ControlRect); //局部刷新绘制 287 } 288 289 290 /// <summary> 291 /// 需要绘制的用于折叠窗口的按钮样式 292 /// </summary> 293 /// <param name="collapse"></param> 294 /// <param name="color"></param> 295 /// <returns></returns> 296 private Bitmap CreateControlImage(bool collapse, Color color) 297 { 298 Bitmap bmp = new Bitmap(80, 9); 299 for (int x = 5; x <= 30; x += 5) 300 { 301 for (int y = 1; y <= 7; y += 3) 302 { 303 bmp.SetPixel(x, y, color); 304 } 305 } 306 for (int x = 50; x <= 75; x += 5) 307 { 308 for (int y = 1; y <= 7; y += 3) 309 { 310 bmp.SetPixel(x, y, color); 311 } 312 } 313 //控制小三角底边向上或者向下 314 if (collapse) 315 { 316 int k = 0; 317 for (int y = 7; y >= 1; y--) 318 { 319 for (int x = 35 + k; x <= 45 - k; x++) 320 { 321 bmp.SetPixel(x, y, color); 322 } 323 k++; 324 } 325 } 326 else 327 { 328 int k = 0; 329 for (int y = 1; y <= 7; y++) 330 { 331 for (int x = 35 + k; x <= 45 - k; x++) 332 { 333 bmp.SetPixel(x, y, color); 334 } 335 k++; 336 } 337 } 338 return bmp; 339 }
可以在工具箱中看到就算完成了。
2.多个自定义splitcontainer
新建一个组件类,名为FreeSplit.cs,继承UserControl类 就可以任意拖动刚才自定义的splitcontainer了。效果如下
3.在tabcontrol中铺满
private int _componentCount = 0; private void toolStripButton4_Click(object sender, EventArgs e) { TabPage tabpage = new TabPage("New" + _userControlCount); propertyGrid.FreeSplit sp = new propertyGrid.FreeSplit(); sp.Parent = tabpage; sp.Dock = DockStyle.Fill;//控件的各个边缘分别停靠在其包含控件的各个边缘,并且适当调整大小 tabControl1.TabPages.Add(tabpage); tabControl1.SelectedIndex = tabControl1.TabPages.Count - 1; }