以前因爲忙於趕項目進度而忽視了軟件的用戶體驗,界面挺難看,有一天看見組長優化了某個窗體,讓人感受徹底不同,我也不甘示弱,要把個人程序作順眼一點才行。個人程序是一個以TabControl爲主要容器的窗體,這樣的程序窗體在目前普遍使用,谷歌瀏覽器Chrome,360安全衛士,QQ,魯大師等。瀏覽器
重點是頭部的TabItem的變遷,從文字到圖標結合文字和單純圖標,讓TabControl以一種比較友好的形式融入到界面中去。先看看控件的效果安全
爲了讓新的TabControl能適應三種狀況(文字,圖標下襯文字,圖標),就定義了以下枚舉,ide
1 public enum TabTypeEnum 2 { 3 ImageText, 4 Text, 5 Image, 6 }
同時在新的TabControl類裏面定義了對應的屬性TabType和私有字段_tabType函數
private TabTypeEnum _tabType; public TabTypeEnum TabType { get { return _tabType; } set { _tabType = value; if (TabType != TabTypeEnum.Text) { SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); base.UpdateStyles(); this.SizeMode = TabSizeMode.Fixed; } else { SizeMode = defaultSizeModel; this.Size = defaultSize; } } }
在改變Tab的類型時要額外加一些處理邏輯,若是是Tab包含圖標的,確定要對控件的Style進行設置優化
SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); base.UpdateStyles();
這裏設置的都與重繪控件時有關:雙緩衝,改變大小則重繪控件。對於單純圖標還有圖標+文字,單純文字這三種方式的Tab大小會有所不一樣,這個Tab的大小經過ItemSize設置,這裏我默認設置了純文字則按回它初始值的大小,這個初始值在構造函數裏獲取;圖標+文字和純圖標這兩種方式在重繪時計算設置。this
描繪控件又是去重寫OnPaint方法,這樣又用回疏遠了好久的GDI+去描繪這個TabItem。這裏有三種Tab方式,但着重介紹圖標+文字這種方式,Tab選中會有陰影的,陰影能夠本身PS一個圓角矩形,我這個是網上摳別人的,這個圖片已添加「已有項」的形式添加到項目中,而後生成操做選「嵌入資源」。spa
而後在構造函數裏面如下面的形式獲取陰影圖片資源操作系統
backImage = new Bitmap(this.GetType(), "select_background.jpg");
在繪圖時,先繪陰影,再繪文字,最後繪圖標。code
獲取當前Tab的矩形主要經過TabControl的GetTabRect(int index)方法,經過判斷當前的Tab是否是被選中的,來決定繪不繪製陰影blog
if (this.SelectedIndex == i) { e.Graphics.DrawImage(backImage, this.GetTabRect(i)); }
而後根據Tab文字的Size來決定TabSize,
if (this.ItemSize.Width < (textSize.Width + this.Padding.X * 2)) this.ItemSize = new System.Drawing.Size((int)textSize.Width + this.Padding.X * 2, this.ItemSize.Height); if (this.ItemSize.Height < (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * 2) new System.Drawing.Size(this.ItemSize.Width, (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * 2);
而後按照文字的Size還有Tab矩形的位置大小計算出文字的位置,描繪出文字
textPoint.X = bounds.X + (bounds.Width - textSize.Width) / 2; textPoint.Y = bounds.Bottom - textSize.Height - this.Padding.Y; e.Graphics.DrawString( this.TabPages[i].Text, this.Font, SystemBrushes.ControlText, textPoint.X, textPoint.Y);
最後描繪圖標也是結合了圖標的Size和Tab的位置與大小來決定圖標所在的位置,
e.Graphics.DrawImage( icon, bounds.X + (bounds.Width - icon.Width) / 2, bounds.Top + this.Padding.Y);
加上了這行代碼,能讓描繪出來的文字少點鋸齒
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
Tab的描繪就完成了,其他兩種Tab只是省去了文字部分或者圖標部分的描繪而已,兩部分的代碼都會在最後列舉整個控件源碼時順帶列舉出來。
這個控件很大程度上參考了CSDN網友的源碼,本來的博文一會兒找不出來,要是哪位園友知道的順帶告訴我,我做爲參考連接附在文中,謝謝!
1 class ImageTabControl:TabControl 2 { 3 public enum TabTypeEnum 4 { 5 ImageText, 6 Text, 7 Image, 8 } 9 10 Image backImage; 11 Size defaultSize; 12 TabSizeMode defaultSizeModel; 13 14 public ImageTabControl() 15 { 16 defaultSize = this.ItemSize; 17 defaultSizeModel = this.SizeMode; 18 19 this.TabType = TabTypeEnum.ImageText; 20 backImage = new Bitmap(this.GetType(), "select_background.jpg"); 21 } 22 23 private TabTypeEnum _tabType; 24 public TabTypeEnum TabType 25 { 26 get { return _tabType; } 27 set 28 { 29 _tabType = value; 30 if (TabType != TabTypeEnum.Text) 31 { 32 SetStyle(ControlStyles.UserPaint | // 控件將自行繪製,而不是經過操做系統來繪製 33 ControlStyles.OptimizedDoubleBuffer | // 該控件首先在緩衝區中繪製,而不是直接繪製到屏幕上,這樣能夠減小閃爍 34 ControlStyles.AllPaintingInWmPaint | // 控件將忽略 WM_ERASEBKGND 窗口消息以減小閃爍 35 ControlStyles.ResizeRedraw | // 在調整控件大小時重繪控件 36 ControlStyles.SupportsTransparentBackColor, // 控件接受 alpha 組件小於 255 的 BackColor 以模擬透明 37 true); 38 base.UpdateStyles(); 39 40 this.SizeMode = TabSizeMode.Fixed; 41 } 42 else 43 { 44 45 SizeMode = defaultSizeModel; 46 this.Size = defaultSize; 47 } 48 } 49 } 50 51 protected override void OnPaint(PaintEventArgs e) 52 { 53 base.OnPaint(e); 54 55 if (TabType == TabTypeEnum.ImageText) 56 DrawImageTextItem(e); 57 else if (TabType == TabTypeEnum.Image) 58 DrawImageItem(e); 59 else if (TabType == TabTypeEnum.Text) 60 DrawTextItem(e); 61 } 62 63 protected virtual void DrawImageTextItem(PaintEventArgs e) 64 { 65 for (int i = 0; i < this.TabCount; i++) 66 { 67 //e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i)); 68 if (this.SelectedIndex == i) 69 { 70 e.Graphics.DrawImage(backImage, this.GetTabRect(i)); 71 } 72 73 // Calculate text position 74 Rectangle bounds = this.GetTabRect(i); 75 PointF textPoint = new PointF(); 76 SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font); 77 78 if (this.ItemSize.Width < (textSize.Width + this.Padding.X * 2)) 79 this.ItemSize = 80 new System.Drawing.Size((int)textSize.Width + this.Padding.X * 2, 81 this.ItemSize.Height); 82 if (this.ItemSize.Height < (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * 2) 83 new System.Drawing.Size(this.ItemSize.Width, 84 (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * 2); 85 86 // 注意要加上每一個標籤的左偏移量X 87 textPoint.X 88 = bounds.X + (bounds.Width - textSize.Width) / 2; 89 textPoint.Y 90 = bounds.Bottom - textSize.Height - this.Padding.Y; 91 92 // Draw highlights 93 e.Graphics.DrawString( 94 this.TabPages[i].Text, 95 this.Font, 96 SystemBrushes.ControlLightLight, // 高光顏色 97 textPoint.X, 98 textPoint.Y); 99 100 // 繪製正常文字 101 textPoint.Y--; 102 e.Graphics.DrawString( 103 this.TabPages[i].Text, 104 this.Font, 105 SystemBrushes.ControlText, // 正常顏色 106 textPoint.X, 107 textPoint.Y); 108 109 110 if (this.ImageList != null) 111 { 112 int index = this.TabPages[i].ImageIndex; 113 string key = this.TabPages[i].ImageKey; 114 Image icon = new Bitmap(1, 1); 115 116 if (index > -1) 117 { 118 icon = this.ImageList.Images[index]; 119 } 120 if (!string.IsNullOrEmpty(key)) 121 { 122 icon = this.ImageList.Images[key]; 123 } 124 e.Graphics.DrawImage( 125 icon, 126 bounds.X + (bounds.Width - icon.Width) / 2, 127 bounds.Top + this.Padding.Y); 128 } 129 } 130 131 e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; 132 } 133 134 protected virtual void DrawImageItem(PaintEventArgs e) 135 { 136 for (int i = 0; i < this.TabPages.Count; i++) 137 { 138 if (i == this.SelectedIndex) 139 { 140 e.Graphics.DrawImage(backImage, this.GetTabRect(i)); 141 } 142 143 RectangleF itemRec = this.GetTabRect(i); 144 145 if (ImageList != null) 146 { 147 int imageIndex=this.TabPages[i].ImageIndex; 148 string imageKey=this.TabPages[i].ImageKey; 149 Image ico=new Bitmap(1,1); 150 if (imageIndex >= 0) 151 ico = this.ImageList.Images[i]; 152 if (!string.IsNullOrEmpty(imageKey)) 153 ico = this.ImageList.Images[imageKey]; 154 155 if (this.ItemSize.Height < ImageList.ImageSize.Height + this.Padding.Y * 2) 156 this.ItemSize = new System.Drawing.Size(this.ItemSize.Width, 157 ImageList.ImageSize.Height + this.Padding.Y * 2); 158 if (this.ItemSize.Width < ImageList.ImageSize.Width + this.Padding.X * 2) 159 this.ItemSize = new System.Drawing.Size(ImageList.ImageSize.Width + this.Padding.X * 2, 160 this.ItemSize.Height); 161 162 e.Graphics.DrawImage(ico, itemRec.X + (itemRec.Width - ico.Width) / 2, itemRec.Y + this.Padding.Y); 163 } 164 } 165 } 166 167 protected virtual void DrawTextItem(PaintEventArgs e) 168 { 169 for (int i = 0; i < this.TabCount; i++) 170 { 171 //e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i)); 172 if (this.SelectedIndex == i) 173 { 174 e.Graphics.DrawImage(backImage, this.GetTabRect(i)); 175 } 176 177 // Calculate text position 178 Rectangle bounds = this.GetTabRect(i); 179 PointF textPoint = new PointF(); 180 SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font); 181 182 // 注意要加上每一個標籤的左偏移量X 183 textPoint.X 184 = bounds.X + (bounds.Width - textSize.Width) / 2; 185 textPoint.Y 186 = bounds.Y+(bounds.Height-textSize.Height)/2; 187 188 // Draw highlights 189 e.Graphics.DrawString( 190 this.TabPages[i].Text, 191 this.Font, 192 SystemBrushes.ControlLightLight, // 高光顏色 193 textPoint.X, 194 textPoint.Y); 195 196 // 繪製正常文字 197 textPoint.Y--; 198 e.Graphics.DrawString( 199 this.TabPages[i].Text, 200 this.Font, 201 SystemBrushes.ControlText, // 正常顏色 202 textPoint.X, 203 textPoint.Y); 204 205 } 206 } 207 }