前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
开源地址:
如果觉得写的还行,请点个 star 支持一下吧
欢迎前来交流探讨: 企鹅群568015492
目录
准备工作
此控件在基础上调整修改,特此感谢
开始
添加一个用户组件,命名tabcontrolext,继承自tabcontrol
几个重写属性
1 private color _backcolor = color.white; 2 [browsable(true)] 3 [editorbrowsable(editorbrowsablestate.always)] 4 [defaultvalue(typeof(color), "white")] 5 public override color backcolor 6 { 7 get { return _backcolor; } 8 set 9 { 10 _backcolor = value; 11 base.invalidate(true); 12 } 13 } 14 15 private color _bordercolor = color.fromargb(232, 232, 232); 16 [defaultvalue(typeof(color), "232, 232, 232")] 17 [description("tabcontorl边框色")] 18 public color bordercolor 19 { 20 get { return _bordercolor; } 21 set 22 { 23 _bordercolor = value; 24 base.invalidate(true); 25 } 26 } 27 28 private color _headselectedbackcolor = color.fromargb(255, 85, 51); 29 [defaultvalue(typeof(color), "255, 85, 51")] 30 [description("tabpage头部选中后的背景颜色")] 31 public color headselectedbackcolor 32 { 33 get { return _headselectedbackcolor; } 34 set { _headselectedbackcolor = value; } 35 } 36 37 private color _headselectedbordercolor = color.fromargb(232, 232, 232); 38 [defaultvalue(typeof(color), "232, 232, 232")] 39 [description("tabpage头部选中后的边框颜色")] 40 public color headselectedbordercolor 41 { 42 get { return _headselectedbordercolor; } 43 set { _headselectedbordercolor = value; } 44 } 45 46 private color _headerbackcolor = color.white; 47 [defaultvalue(typeof(color), "white")] 48 [description("tabpage头部默认背景颜色")] 49 public color headerbackcolor 50 { 51 get { return _headerbackcolor; } 52 set { _headerbackcolor = value; } 53 }
重写背景
1 protected override void onpaintbackground(painteventargs pevent) 2 { 3 if (this.designmode == true) 4 { 5 lineargradientbrush backbrush = new lineargradientbrush( 6 this.bounds, 7 systemcolors.controllightlight, 8 systemcolors.controllight, 9 lineargradientmode.vertical); 10 pevent.graphics.fillrectangle(backbrush, this.bounds); 11 backbrush.dispose(); 12 } 13 else 14 { 15 this.painttransparentbackground(pevent.graphics, this.clientrectangle); 16 } 17 } 18 /// <summary> 19 /// tabcontorl 背景色设置 20 /// </summary> 21 /// <param name="g"></param> 22 /// <param name="cliprect"></param> 23 protected void painttransparentbackground(graphics g, rectangle cliprect) 24 { 25 if ((this.parent != null)) 26 { 27 cliprect.offset(this.location); 28 painteventargs e = new painteventargs(g, cliprect); 29 graphicsstate state = g.save(); 30 g.smoothingmode = smoothingmode.highspeed; 31 try 32 { 33 g.translatetransform((float)-this.location.x, (float)-this.location.y); 34 this.invokepaintbackground(this.parent, e); 35 this.invokepaint(this.parent, e); 36 } 37 finally 38 { 39 g.restore(state); 40 cliprect.offset(-this.location.x, -this.location.y); 41 //新加片段,待测试 42 using (solidbrush brush = new solidbrush(_backcolor)) 43 { 44 cliprect.inflate(1, 1); 45 g.fillrectangle(brush, cliprect); 46 } 47 } 48 } 49 else 50 { 51 system.drawing.drawing2d.lineargradientbrush backbrush = new system.drawing.drawing2d.lineargradientbrush(this.bounds, systemcolors.controllightlight, systemcolors.controllight, system.drawing.drawing2d.lineargradientmode.vertical); 52 g.fillrectangle(backbrush, this.bounds); 53 backbrush.dispose(); 54 } 55 }
重绘
1 protected override void onpaint(painteventargs e) 2 { 3 // paint the background 4 base.onpaint(e); 5 this.painttransparentbackground(e.graphics, this.clientrectangle); 6 this.paintallthetabs(e); 7 this.paintthetabpageborder(e); 8 this.painttheselectedtab(e); 9 }
辅助函数
1 private void paintallthetabs(system.windows.forms.painteventargs e) 2 { 3 if (this.tabcount > 0) 4 { 5 for (int index = 0; index < this.tabcount; index++) 6 { 7 this.painttab(e, index); 8 } 9 } 10 } 11 12 private void painttab(system.windows.forms.painteventargs e, int index) 13 { 14 graphicspath path = this.gettabpath(index); 15 this.painttabbackground(e.graphics, index, path); 16 this.painttabborder(e.graphics, index, path); 17 this.painttabtext(e.graphics, index); 18 this.painttabimage(e.graphics, index); 19 } 20 21 /// <summary> 22 /// 设置选项卡头部颜色 23 /// </summary> 24 /// <param name="graph"></param> 25 /// <param name="index"></param> 26 /// <param name="path"></param> 27 private void painttabbackground(system.drawing.graphics graph, int index, system.drawing.drawing2d.graphicspath path) 28 { 29 rectangle rect = this.gettabrect(index); 30 system.drawing.brush buttonbrush = new system.drawing.drawing2d.lineargradientbrush(rect, _headerbackcolor, _headerbackcolor, lineargradientmode.vertical); //非选中时候的 tabpage 页头部背景色 31 graph.fillpath(buttonbrush, path); 32 //if (index == this.selectedindex) 33 //{ 34 // //buttonbrush = new system.drawing.solidbrush(_headselectedbackcolor); 35 // graph.drawline(new pen(_headerbackcolor), rect.right+2, rect.bottom, rect.left + 1, rect.bottom); 36 //} 37 buttonbrush.dispose(); 38 } 39 40 /// <summary> 41 /// 设置选项卡头部边框色 42 /// </summary> 43 /// <param name="graph"></param> 44 /// <param name="index"></param> 45 /// <param name="path"></param> 46 private void painttabborder(system.drawing.graphics graph, int index, system.drawing.drawing2d.graphicspath path) 47 { 48 pen borderpen = new pen(_bordercolor);// tabpage 非选中时候的 tabpage 头部边框色 49 if (index == this.selectedindex) 50 { 51 borderpen = new pen(_headselectedbordercolor); // tabpage 选中后的 tabpage 头部边框色 52 } 53 graph.drawpath(borderpen, path); 54 borderpen.dispose(); 55 } 56 57 private void painttabimage(system.drawing.graphics g, int index) 58 { 59 image tabimage = null; 60 if (this.tabpages[index].imageindex > -1 && this.imagelist != null) 61 { 62 tabimage = this.imagelist.images[this.tabpages[index].imageindex]; 63 } 64 else if (this.tabpages[index].imagekey.trim().length > 0 && this.imagelist != null) 65 { 66 tabimage = this.imagelist.images[this.tabpages[index].imagekey]; 67 } 68 if (tabimage != null) 69 { 70 rectangle rect = this.gettabrect(index); 71 g.drawimage(tabimage, rect.right - rect.height - 4, 4, rect.height - 2, rect.height - 2); 72 } 73 } 74 75 private void painttabtext(system.drawing.graphics graph, int index) 76 { 77 string tabtext = this.tabpages[index].text; 78 79 system.drawing.stringformat format = new system.drawing.stringformat(); 80 format.alignment = stringalignment.near; 81 format.linealignment = stringalignment.center; 82 format.trimming = stringtrimming.ellipsischaracter; 83 84 brush forebrush = null; 85 86 if (this.tabpages[index].enabled == false) 87 { 88 forebrush = systembrushes.controldark; 89 } 90 else 91 { 92 forebrush = systembrushes.controltext; 93 } 94 95 font tabfont = this.font; 96 if (index == this.selectedindex) 97 { 98 if (this.tabpages[index].enabled != false) 99 { 100 forebrush = new solidbrush(_headselectedbackcolor); 101 } 102 } 103 104 rectangle rect = this.gettabrect(index); 105 106 var txtsize = controlhelper.getstringwidth(tabtext, graph, tabfont); 107 rectangle rect2 = new rectangle(rect.left + (rect.width - txtsize) / 2 - 1, rect.top, rect.width, rect.height); 108 109 graph.drawstring(tabtext, tabfont, forebrush, rect2, format); 110 } 111 112 /// <summary> 113 /// 设置 tabpage 内容页边框色 114 /// </summary> 115 /// <param name="e"></param> 116 private void paintthetabpageborder(system.windows.forms.painteventargs e) 117 { 118 if (this.tabcount > 0) 119 { 120 rectangle borderrect = this.tabpages[0].bounds; 121 //borderrect.inflate(1, 1); 122 rectangle rect = new rectangle(borderrect.x - 2, borderrect.y-1, borderrect.width + 5, borderrect.height+2); 123 controlpaint.drawborder(e.graphics, rect, this.bordercolor, buttonborderstyle.solid); 124 } 125 } 126 127 /// <summary> 128 /// // tabpage 页头部间隔色 129 /// </summary> 130 /// <param name="e"></param> 131 private void painttheselectedtab(system.windows.forms.painteventargs e) 132 { 133 if (this.selectedindex == -1) 134 return; 135 rectangle selrect; 136 int selrectright = 0; 137 selrect = this.gettabrect(this.selectedindex); 138 selrectright = selrect.right; 139 e.graphics.drawline(new pen(_headselectedbackcolor), selrect.left, selrect.bottom + 1, selrectright, selrect.bottom + 1); 140 } 141 142 private graphicspath gettabpath(int index) 143 { 144 system.drawing.drawing2d.graphicspath path = new system.drawing.drawing2d.graphicspath(); 145 path.reset(); 146 147 rectangle rect = this.gettabrect(index); 148 149 switch (alignment) 150 { 151 case tabalignment.top: 152 153 break; 154 case tabalignment.bottom: 155 156 break; 157 case tabalignment.left: 158 159 break; 160 case tabalignment.right: 161 162 break; 163 } 164 165 path.addline(rect.left, rect.top, rect.left, rect.bottom + 1); 166 path.addline(rect.left, rect.top, rect.right , rect.top); 167 path.addline(rect.right , rect.top, rect.right , rect.bottom + 1); 168 path.addline(rect.right , rect.bottom + 1, rect.left, rect.bottom + 1); 169 170 return path; 171 }
2个重写函数处理字体相关
1 [dllimport("user32.dll")] 2 private static extern intptr sendmessage(intptr hwnd, int msg, intptr wparam, intptr lparam); 3 4 private const int wm_setfont = 0x30; 5 private const int wm_fontchange = 0x1d; 6 7 protected override void oncreatecontrol() 8 { 9 base.oncreatecontrol(); 10 this.onfontchanged(eventargs.empty); 11 } 12 13 protected override void onfontchanged(eventargs e) 14 { 15 base.onfontchanged(e); 16 intptr hfont = this.font.tohfont(); 17 sendmessage(this.handle, wm_setfont, hfont, (intptr)(-1)); 18 sendmessage(this.handle, wm_fontchange, intptr.zero, intptr.zero); 19 this.updatestyles(); 20 }
完整代码
1 using system; 2 using system.collections.generic; 3 using system.componentmodel; 4 using system.diagnostics; 5 using system.drawing; 6 using system.drawing.drawing2d; 7 using system.linq; 8 using system.runtime.interopservices; 9 using system.text; 10 using system.windows.forms; 11 12 namespace hzh_controls.controls 13 { 14 public class tabcontrolext : tabcontrol 15 { 16 public tabcontrolext() 17 : base() 18 { 19 setstyles(); 20 this.multiline = true; 21 this.itemsize = new size(this.itemsize.width, 50); 22 } 23 24 private void setstyles() 25 { 26 base.setstyle( 27 controlstyles.userpaint | 28 controlstyles.doublebuffer | 29 controlstyles.optimizeddoublebuffer | 30 controlstyles.allpaintinginwmpaint | 31 controlstyles.resizeredraw | 32 controlstyles.supportstransparentbackcolor, true); 33 base.updatestyles(); 34 } 35 36 private color _backcolor = color.white; 37 [browsable(true)] 38 [editorbrowsable(editorbrowsablestate.always)] 39 [defaultvalue(typeof(color), "white")] 40 public override color backcolor 41 { 42 get { return _backcolor; } 43 set 44 { 45 _backcolor = value; 46 base.invalidate(true); 47 } 48 } 49 50 private color _bordercolor = color.fromargb(232, 232, 232); 51 [defaultvalue(typeof(color), "232, 232, 232")] 52 [description("tabcontorl边框色")] 53 public color bordercolor 54 { 55 get { return _bordercolor; } 56 set 57 { 58 _bordercolor = value; 59 base.invalidate(true); 60 } 61 } 62 63 private color _headselectedbackcolor = color.fromargb(255, 85, 51); 64 [defaultvalue(typeof(color), "255, 85, 51")] 65 [description("tabpage头部选中后的背景颜色")] 66 public color headselectedbackcolor 67 { 68 get { return _headselectedbackcolor; } 69 set { _headselectedbackcolor = value; } 70 } 71 72 private color _headselectedbordercolor = color.fromargb(232, 232, 232); 73 [defaultvalue(typeof(color), "232, 232, 232")] 74 [description("tabpage头部选中后的边框颜色")] 75 public color headselectedbordercolor 76 { 77 get { return _headselectedbordercolor; } 78 set { _headselectedbordercolor = value; } 79 } 80 81 private color _headerbackcolor = color.white; 82 [defaultvalue(typeof(color), "white")] 83 [description("tabpage头部默认背景颜色")] 84 public color headerbackcolor 85 { 86 get { return _headerbackcolor; } 87 set { _headerbackcolor = value; } 88 } 89 90 protected override void onpaintbackground(painteventargs pevent) 91 { 92 if (this.designmode == true) 93 { 94 lineargradientbrush backbrush = new lineargradientbrush( 95 this.bounds, 96 systemcolors.controllightlight, 97 systemcolors.controllight, 98 lineargradientmode.vertical); 99 pevent.graphics.fillrectangle(backbrush, this.bounds); 100 backbrush.dispose(); 101 } 102 else 103 { 104 this.painttransparentbackground(pevent.graphics, this.clientrectangle); 105 } 106 } 107 108 /// <summary> 109 /// tabcontorl 背景色设置 110 /// </summary> 111 /// <param name="g"></param> 112 /// <param name="cliprect"></param> 113 protected void painttransparentbackground(graphics g, rectangle cliprect) 114 { 115 if ((this.parent != null)) 116 { 117 cliprect.offset(this.location); 118 painteventargs e = new painteventargs(g, cliprect); 119 graphicsstate state = g.save(); 120 g.smoothingmode = smoothingmode.highspeed; 121 try 122 { 123 g.translatetransform((float)-this.location.x, (float)-this.location.y); 124 this.invokepaintbackground(this.parent, e); 125 this.invokepaint(this.parent, e); 126 } 127 finally 128 { 129 g.restore(state); 130 cliprect.offset(-this.location.x, -this.location.y); 131 //新加片段,待测试 132 using (solidbrush brush = new solidbrush(_backcolor)) 133 { 134 cliprect.inflate(1, 1); 135 g.fillrectangle(brush, cliprect); 136 } 137 } 138 } 139 else 140 { 141 system.drawing.drawing2d.lineargradientbrush backbrush = new system.drawing.drawing2d.lineargradientbrush(this.bounds, systemcolors.controllightlight, systemcolors.controllight, system.drawing.drawing2d.lineargradientmode.vertical); 142 g.fillrectangle(backbrush, this.bounds); 143 backbrush.dispose(); 144 } 145 } 146 147 protected override void onpaint(painteventargs e) 148 { 149 // paint the background 150 base.onpaint(e); 151 this.painttransparentbackground(e.graphics, this.clientrectangle); 152 this.paintallthetabs(e); 153 this.paintthetabpageborder(e); 154 this.painttheselectedtab(e); 155 } 156 157 private void paintallthetabs(system.windows.forms.painteventargs e) 158 { 159 if (this.tabcount > 0) 160 { 161 for (int index = 0; index < this.tabcount; index++) 162 { 163 this.painttab(e, index); 164 } 165 } 166 } 167 168 private void painttab(system.windows.forms.painteventargs e, int index) 169 { 170 graphicspath path = this.gettabpath(index); 171 this.painttabbackground(e.graphics, index, path); 172 this.painttabborder(e.graphics, index, path); 173 this.painttabtext(e.graphics, index); 174 this.painttabimage(e.graphics, index); 175 } 176 177 /// <summary> 178 /// 设置选项卡头部颜色 179 /// </summary> 180 /// <param name="graph"></param> 181 /// <param name="index"></param> 182 /// <param name="path"></param> 183 private void painttabbackground(system.drawing.graphics graph, int index, system.drawing.drawing2d.graphicspath path) 184 { 185 rectangle rect = this.gettabrect(index); 186 system.drawing.brush buttonbrush = new system.drawing.drawing2d.lineargradientbrush(rect, _headerbackcolor, _headerbackcolor, lineargradientmode.vertical); //非选中时候的 tabpage 页头部背景色 187 graph.fillpath(buttonbrush, path); 188 //if (index == this.selectedindex) 189 //{ 190 // //buttonbrush = new system.drawing.solidbrush(_headselectedbackcolor); 191 // graph.drawline(new pen(_headerbackcolor), rect.right+2, rect.bottom, rect.left + 1, rect.bottom); 192 //} 193 buttonbrush.dispose(); 194 } 195 196 /// <summary> 197 /// 设置选项卡头部边框色 198 /// </summary> 199 /// <param name="graph"></param> 200 /// <param name="index"></param> 201 /// <param name="path"></param> 202 private void painttabborder(system.drawing.graphics graph, int index, system.drawing.drawing2d.graphicspath path) 203 { 204 pen borderpen = new pen(_bordercolor);// tabpage 非选中时候的 tabpage 头部边框色 205 if (index == this.selectedindex) 206 { 207 borderpen = new pen(_headselectedbordercolor); // tabpage 选中后的 tabpage 头部边框色 208 } 209 graph.drawpath(borderpen, path); 210 borderpen.dispose(); 211 } 212 213 private void painttabimage(system.drawing.graphics g, int index) 214 { 215 image tabimage = null; 216 if (this.tabpages[index].imageindex > -1 && this.imagelist != null) 217 { 218 tabimage = this.imagelist.images[this.tabpages[index].imageindex]; 219 } 220 else if (this.tabpages[index].imagekey.trim().length > 0 && this.imagelist != null) 221 { 222 tabimage = this.imagelist.images[this.tabpages[index].imagekey]; 223 } 224 if (tabimage != null) 225 { 226 rectangle rect = this.gettabrect(index); 227 g.drawimage(tabimage, rect.right - rect.height - 4, 4, rect.height - 2, rect.height - 2); 228 } 229 } 230 231 private void painttabtext(system.drawing.graphics graph, int index) 232 { 233 string tabtext = this.tabpages[index].text; 234 235 system.drawing.stringformat format = new system.drawing.stringformat(); 236 format.alignment = stringalignment.near; 237 format.linealignment = stringalignment.center; 238 format.trimming = stringtrimming.ellipsischaracter; 239 240 brush forebrush = null; 241 242 if (this.tabpages[index].enabled == false) 243 { 244 forebrush = systembrushes.controldark; 245 } 246 else 247 { 248 forebrush = systembrushes.controltext; 249 } 250 251 font tabfont = this.font; 252 if (index == this.selectedindex) 253 { 254 if (this.tabpages[index].enabled != false) 255 { 256 forebrush = new solidbrush(_headselectedbackcolor); 257 } 258 } 259 260 rectangle rect = this.gettabrect(index); 261 262 var txtsize = controlhelper.getstringwidth(tabtext, graph, tabfont); 263 rectangle rect2 = new rectangle(rect.left + (rect.width - txtsize) / 2 - 1, rect.top, rect.width, rect.height); 264 265 graph.drawstring(tabtext, tabfont, forebrush, rect2, format); 266 } 267 268 /// <summary> 269 /// 设置 tabpage 内容页边框色 270 /// </summary> 271 /// <param name="e"></param> 272 private void paintthetabpageborder(system.windows.forms.painteventargs e) 273 { 274 if (this.tabcount > 0) 275 { 276 rectangle borderrect = this.tabpages[0].bounds; 277 //borderrect.inflate(1, 1); 278 rectangle rect = new rectangle(borderrect.x - 2, borderrect.y - 1, borderrect.width + 5, borderrect.height + 2); 279 controlpaint.drawborder(e.graphics, rect, this.bordercolor, buttonborderstyle.solid); 280 } 281 } 282 283 /// <summary> 284 /// // tabpage 页头部间隔色 285 /// </summary> 286 /// <param name="e"></param> 287 private void painttheselectedtab(system.windows.forms.painteventargs e) 288 { 289 if (this.selectedindex == -1) 290 return; 291 rectangle selrect; 292 int selrectright = 0; 293 selrect = this.gettabrect(this.selectedindex); 294 selrectright = selrect.right; 295 e.graphics.drawline(new pen(_headselectedbackcolor), selrect.left, selrect.bottom + 1, selrectright, selrect.bottom + 1); 296 } 297 298 private graphicspath gettabpath(int index) 299 { 300 system.drawing.drawing2d.graphicspath path = new system.drawing.drawing2d.graphicspath(); 301 path.reset(); 302 303 rectangle rect = this.gettabrect(index); 304 305 switch (alignment) 306 { 307 case tabalignment.top: 308 309 break; 310 case tabalignment.bottom: 311 312 break; 313 case tabalignment.left: 314 315 break; 316 case tabalignment.right: 317 318 break; 319 } 320 321 path.addline(rect.left, rect.top, rect.left, rect.bottom + 1); 322 path.addline(rect.left, rect.top, rect.right, rect.top); 323 path.addline(rect.right, rect.top, rect.right, rect.bottom + 1); 324 path.addline(rect.right, rect.bottom + 1, rect.left, rect.bottom + 1); 325 326 return path; 327 } 328 329 [dllimport("user32.dll")] 330 private static extern intptr sendmessage(intptr hwnd, int msg, intptr wparam, intptr lparam); 331 332 private const int wm_setfont = 0x30; 333 private const int wm_fontchange = 0x1d; 334 335 protected override void oncreatecontrol() 336 { 337 base.oncreatecontrol(); 338 this.onfontchanged(eventargs.empty); 339 } 340 341 protected override void onfontchanged(eventargs e) 342 { 343 base.onfontchanged(e); 344 intptr hfont = this.font.tohfont(); 345 sendmessage(this.handle, wm_setfont, hfont, (intptr)(-1)); 346 sendmessage(this.handle, wm_fontchange, intptr.zero, intptr.zero); 347 this.updatestyles(); 348 } 349 } 350 }
用处及效果
最后的话
如果你喜欢的话,请到 点个星 星吧
黄山市民网:https://www.huangshanshimin.com/