在网上自绘tabcontrol的基础上增强,大部分代码写了注释。

关闭按钮的绘制思路:对padding属性修改以增加标签空白部分。在空白部分绘制十字,写个鼠标在十字区域点击关闭的事件以在适用控件时处理一些必要事情。

tip小字思路:需要显示TIP时,在标签合适位置绘制半透明背景,同时绘制需要显示的TIP文字。对控件增加个需要绘制标签的容器以储存需要绘制TIP的标签及相应文字。写个公用方法在外部调用以将需要绘制tip的标签名和文字传递给控件。

呼吸灯效果思路:以timer控件控制标签背景色的改变从而实现动画效果。对控件增加个容器以储存需要呼吸灯效果的标签名,绘制时标签名判断是否在容器内从而决定是否对标签背景色进行变化。

资源下载:点击打开链接

using System.Collections.Generic;
using System;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.IO;namespace MyTabControl
{public partial class MyTabControlEx:TabControl{#region 本地参数/// <summary>/// 以下三行数据为呼吸灯相关过程数据/// </summary>int key = 1;float step = 10;int ima = 0, br, bg, bb;      /// <summary>/// 呼吸灯传递过程颜色/// </summary>private Color _tabbaseColor = Color.FromArgb(166, 222, 255);//标签颜色/// <summary>/// 基础颜色/// </summary>private Color _baseColor = Color.FromArgb(166, 222, 255);//标签颜色/// <summary>/// 背景色/// </summary>private Color _backColor = Color.FromArgb(234, 247, 254);//背景色private Color _borderColor = Color.FromArgb(23, 169, 254);//框线颜色private Color _flashColor = Color.LightYellow;//呼吸灯效果颜色private Color _tipBackColor = Color.DarkBlue;//标签tip的背景色private Color _tipTextColor = Color.White;//标签tip的字体颜色/// <summary>/// tab标签圆角直径/// </summary>private static readonly int Radius = 8;private bool _haveCloseButton=false;private bool _drawTipText = false;private int _lastSelectIndex=-1;private int _selectIndex=0;private Timer flashTime = new Timer();//控制标签页闪烁的时间控件     /// <summary>/// 具有呼吸灯效果标签的数组容器/// </summary>Dictionary<string,bool> tabFlickerDictionary=new Dictionary<string,bool>();/// <summary>/// 记录标签Tip文字的容器/// </summary>Dictionary<string,string> TipTextDictionary = new Dictionary<string,string>();#endregion#region 构造函数/// <summary>/// 构造函数/// </summary>public MyTabControlEx(): base(){flashTime.Interval = 50;//控制呼吸灯效果的时间控件flashTime.Enabled=false;            flashTime.Tick += new EventHandler(tabFlicker_timer_Tick);_tabbaseColor = _baseColor;if (TabPages.Count > 0) _lastSelectIndex = 0;SetStyles();}#endregion#region 事件  /// <summary>/// 事件委托/// </summary>/// <param name="sender">传递本tabControl的指针</param>/// <param name="e"></param>public delegate void tabCloseEventHandle(object sender, MouseEventArgs e);/// <summary>/// 选中标签页在关闭前触发事件(tabcontrol的select标签页关闭前触发),与controlremoved事件相似/// </summary>[Browsable(true)][Description("标签页点击关闭按钮后,关闭前触发的事件")][EditorBrowsable(EditorBrowsableState.Always)]public event tabCloseEventHandle tabClose;#endregion#region 属性/// <summary>/// 控件基础颜色/// </summary>[Category("外观")][Description("控件标签基础颜色")][DefaultValue(typeof(Color), "166, 222, 255")]public Color BaseColor{get { return _baseColor; }set{_baseColor = value;base.Invalidate(true);}}/// <summary>/// 背景色/// </summary>[Browsable(true)][Category("外观")][Description("控件背景颜色")][EditorBrowsable(EditorBrowsableState.Always)][DefaultValue(typeof(Color), "234, 247, 254")]public override Color BackColor{get { return _backColor; }set{_backColor = value;base.Invalidate(true);}}/// <summary>/// 边框线条颜色/// </summary>[Category("外观")][Description("控件边框颜色")][DefaultValue(typeof(Color), "23, 169, 254")]public Color BorderColor{get { return _borderColor; }set{_borderColor = value;base.Invalidate(true);}}/// <summary>/// Tab标签呼吸灯效果的颜色。/// </summary>[Category("外观")][Description("控件标签呼吸灯颜色")][DefaultValue(typeof(Color), "0, 95, 152")]public Color 标签呼吸灯颜色{get { return _flashColor; }set{_flashColor = value;               }}/// <summary>/// 标签tip背景颜色/// </summary>[Category("外观")][Description("标签tip背景颜色")][DefaultValue(typeof(Color), "0, 128, 255")]public Color TipBackColor{get { return _tipBackColor; }set{_tipBackColor = value;}}/// <summary>/// 标签tip字体颜色/// </summary>[Category("外观")][Description("标签tip字体颜色")][DefaultValue(typeof(Color), "255, 255, 255")]public Color TipTextColor{get { return _tipTextColor; }set{_tipTextColor = value;}}       /// <summary>/// 是否绘制关闭按钮/// </summary>[Category("外观")][Description("是否绘制标签的关闭按钮")][DefaultValue(typeof(bool), "false")]public bool HaveCloseButton{get {return _haveCloseButton; }set{_haveCloseButton = value;if (_haveCloseButton)    //绘制关闭按钮后对按钮周围空间量进行调整{this.Padding = new Point(9, 3);}else { this.Padding = new Point(6, 3); }}}/// <summary>/// 是否在标签绘制提示/// </summary>[Category("外观")][Description("是否绘制标签提示小字")][DefaultValue(typeof(bool), "false")]public bool ShowDrawTipText{get { return _drawTipText; }set{_drawTipText= value;this.Padding = new Point(this.Padding.X,4)  ;}}#endregion#region 对控件部分方法重写       /// <summary>/// 对SelectedIndexChanged重写,在标签改变时记录上次选中的标签;移除当前标签特效/// </summary>/// <param name="e"></param>protected override void OnSelectedIndexChanged(EventArgs e){SelectIndexLog();//以下判断语句移除当前选择项的呼吸灯效果和标签显示效果if (TabPages.Count > 0&SelectedTab!=null){if (tabFlickerDictionary.ContainsKey(SelectedTab.Name)) tabFlickerRemove(SelectedTab.Name);if (TipTextDictionary.ContainsKey(SelectedTab.Name)) TipTextDictionary.Remove(SelectedTab.Name);}else if (TabPages.Count <= 0){tabFlickerDictionary.Clear(); TipTextDictionary.Clear();}    base.OnSelectedIndexChanged(e);}/// <summary>/// 重写鼠标事件,鼠标在关闭区域点击时关闭选项卡/// </summary>/// <param name="e"></param>protected override void OnMouseUp(MouseEventArgs e){if (_haveCloseButton){if (e.Button == MouseButtons.Left){                   Point cusorPoint = PointToClient(MousePosition);//计算关闭区域  Rectangle CloseRect =getCloseButtonRect( this.GetTabRect(SelectedIndex));TabPage checkTab = this.SelectedTab;//如果鼠标在区域内就关闭选项卡                      if (CloseRect.Contains(cusorPoint)){                        if (tabClose != null)//移除选项卡之前执行tabclose事件,此事件在外部调用以在关闭标签时执行用户命令{ tabClose(this, e); }                       SelectedIndex = _lastSelectIndex;      //此为设置选项卡为最后一次选择项,                 this.TabPages.Remove(checkTab);SelectIndexLog();}}}base.OnMouseUp(e);}protected override void OnMouseMove(MouseEventArgs e){base.OnMouseMove(e);base.Invalidate();}protected override void OnMouseLeave(EventArgs e){base.OnMouseLeave(e);base.Invalidate();}/// <summary>/// 重写绘制事件,执行自定义的标签绘制。关键事件/// </summary>/// <param name="e"></param>protected override void OnPaint(PaintEventArgs e){base.OnPaint(e);DrawTabContrl(e.Graphics);}#endregion#region 公开的自定义方法,在外部调用实现闪烁标签和标签TIP功能/// <summary>/// 将tabpage加入呼吸灯显示容器,使标签具有呼吸灯效果/// </summary>/// <param name="tabPageName"></param>public void tabFlickerAdd(string tabPageName){if (SelectedTab != null){if (SelectedTab.Name == tabPageName)    //不对当前标签添加呼吸灯效果{ return; }}if (TabPages.ContainsKey(tabPageName)){if (!tabFlickerDictionary.ContainsKey(tabPageName)){tabFlickerDictionary.Add(tabPageName, false);if (!flashTime.Enabled) flashTime.Enabled = true;}}}/// <summary>/// 将tabpage移除呼吸灯显示容器/// </summary>/// <param name="tabPageName"></param>public void tabFlickerRemove(string tabPageName){            if (tabFlickerDictionary.ContainsKey(tabPageName)){tabFlickerDictionary.Remove(tabPageName);}if (tabFlickerDictionary.Count <= 0) flashTime.Enabled = false;}/// <summary>/// 将需要显示的Tip文字添加到容器以显示/// </summary>/// <param name="tabPageName"></param>/// <param name="text"></param>public void TipTextAdd(string tabPageName, string text){if (SelectedTab != null){if (SelectedTab.Name == tabPageName)    //不对当前标签添加Tip文字{ return; }}if (TabPages.ContainsKey(tabPageName)){if (!TipTextDictionary.ContainsKey(tabPageName)){TipTextDictionary.Add(tabPageName, text);}else{TipTextDictionary[tabPageName] = text;}}}/// <summary>/// Tip文字不需要显示后从容器移除/// </summary>/// <param name="tabPageName"></param>public void TipTextRemove(string tabPageName){if (TipTextDictionary.ContainsKey(tabPageName)){TipTextDictionary.Remove(tabPageName);}}#endregion#region 过程方法/// <summary>/// 对控件基础功能设置。自行绘制,双重缓存忽略WM_ERASEBKGND消息,调整大小重绘,接受ALPHA透明设置/// </summary>private void SetStyles(){base.SetStyle(ControlStyles.UserPaint |                      // 控件将自行绘制,而不是通过操作系统来绘制  ControlStyles.OptimizedDoubleBuffer |          // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁  ControlStyles.AllPaintingInWmPaint |           // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁  ControlStyles.ResizeRedraw |                   // 在调整控件大小时重绘控件  ControlStyles.SupportsTransparentBackColor,    // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明  true);                                    // 设置以上值为 true  base.UpdateStyles();}/// <summary>/// 整个控件重绘方法/// </summary>/// <param name="g"></param>private void DrawTabContrl(Graphics g){g.SmoothingMode = SmoothingMode.AntiAlias;g.InterpolationMode = InterpolationMode.HighQualityBilinear;g.TextRenderingHint = TextRenderingHint.AntiAlias;DrawDrawBackgroundAndHeader(g);DrawTabPages(g);DrawBorder(g);}/// <summary>/// 绘制整体背景/// </summary>/// <param name="g">tabcontrol的绘图面</param>private void DrawDrawBackgroundAndHeader(Graphics g){int x = 0;int y = 0;int width = 0;int height = 0;switch (Alignment){case TabAlignment.Top:x = 0;y = 0;width = ClientRectangle.Width;height = ClientRectangle.Height - DisplayRectangle.Height;                  break;case TabAlignment.Bottom:x = 0;y = DisplayRectangle.Height;width = ClientRectangle.Width;height = ClientRectangle.Height - DisplayRectangle.Height;break;case TabAlignment.Left:x = 0;y = 0;width = ClientRectangle.Width - DisplayRectangle.Width;height = ClientRectangle.Height;break;case TabAlignment.Right:x = DisplayRectangle.Width;y = 0;width = ClientRectangle.Width - DisplayRectangle.Width;height = ClientRectangle.Height;break;}//标签所在的矩形Rectangle headerRect = new Rectangle(x, y, width, height);Color backColor = Enabled ? _backColor : SystemColors.Control;using (SolidBrush brush = new SolidBrush(backColor)){g.FillRectangle(brush, ClientRectangle);g.FillRectangle(brush, headerRect);}}/// <summary>/// 绘制tab标签/// </summary>/// <param name="g"></param>private void DrawTabPages(Graphics g){Rectangle tabRect;Point cusorPoint = PointToClient(MousePosition);bool hover,hadSetClip=false;bool selected;bool alignHorizontal =(Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom);LinearGradientMode mode = alignHorizontal ?LinearGradientMode.Vertical : LinearGradientMode.Horizontal;IntPtr updownHandle = UpDownButtonHandle;if (updownHandle!=IntPtr.Zero)此段代码重设标签绘图区域,将左右键区域排除在外,消除重叠绘图的错误{Rectangle ExludeRect = upDownRect(updownHandle);Rectangle tempClip = new Rectangle();tempClip.X = ClientRectangle.X;tempClip.Y = ClientRectangle.Y;tempClip.Width = ClientRectangle.Width - ExludeRect.Width;tempClip.Height = ClientRectangle.Height;g.SetClip(tempClip);                hadSetClip = true;}for (int index = 0; index < base.TabCount; index++){TabPage page = TabPages[index];tabRect = GetTabRect(index);Bitmap bmp = new Bitmap(tabRect.Width, tabRect.Height);      //使用双重缓存,先将标签绘制到bmp,然后再绘制到界面,防止界面闪烁         Rectangle tmpRect = new Rectangle(0, 0, tabRect.Width, tabRect.Height);if (Alignment == TabAlignment.Bottom)   //在底部的时候,重新绘制标签会导致工作区和标签区重叠的边线绘图错误,不对重叠部分绘制{tmpRect = new Rectangle(0, -1, tabRect.Width, tabRect.Height);}else if (Alignment == TabAlignment.Left || Alignment == TabAlignment.Right)//绘制不了边线,将区域扩大以绘制边线{bmp = new Bitmap(tabRect.Width + 1, tabRect.Height + 1);}Graphics gt = Graphics.FromImage(bmp);             gt.SmoothingMode = SmoothingMode.AntiAlias;gt.InterpolationMode = InterpolationMode.HighQualityBilinear;gt.TextRenderingHint = TextRenderingHint.AntiAlias;hover = tabRect.Contains(cusorPoint);selected = SelectedIndex == index;Color baseColor = _baseColor;if (tabFlickerDictionary.ContainsKey(page.Name)){baseColor = _tabbaseColor;}Color borderColor = _borderColor;if (selected){baseColor = GetColor(_baseColor, 0, -45, -30, -14);}else if (hover){baseColor = GetColor(_baseColor, 0, 35, 24, 9);}RenderTabBackgroundInternal(gt,tmpRect,baseColor,borderColor,.45F,mode);bool hasImage = DrawTabImage(gt, page, tmpRect);DrawtabText(gt, page, tmpRect, hasImage);此段代码尝试绘制关闭图标        if (HaveCloseButton){Point temPoint = new Point(cusorPoint.X - tabRect.X, cusorPoint.Y - tabRect.Y);DrawCloseButton(gt, tmpRect,temPoint);}以下绘制TipText               if (ShowDrawTipText & TipTextDictionary.ContainsKey(page.Name)){if (TipTextDictionary[page.Name] != null){string text = TipTextDictionary[page.Name];if (text != "")DrawTipText(gt, tmpRect, text);}}if (Alignment == TabAlignment.Bottom){ g.DrawImage(bmp, tabRect.X, tabRect.Y + 1); }elseg.DrawImage(bmp, tabRect.X, tabRect.Y);bmp.Dispose();gt.Dispose();}if (hadSetClip) g.ResetClip();}/// <summary>/// 按时间重设呼吸灯效果颜色/// </summary>/// <param name="sender"></param>/// <param name="e"></param>public void tabFlicker_timer_Tick(object sender,EventArgs e){ima=ima+key;if (ima >= step | ima <= 0) key = key * -1;           Color flashColor = _flashColor;br = (int)(-(_baseColor.R - flashColor.R) / step * ima);bb = (int)(-(_baseColor.B - flashColor.B) / step * ima);bg = (int)(-(_baseColor.G - flashColor.G) / step * ima);          _tabbaseColor=GetColor(_baseColor,0,br,bg,bb);           Graphics g = Graphics.FromHwnd(this.Handle);if (g!=null)  DrawTabPages(g);          }/// <summary>/// 根据标签页矩形区,计算关闭按钮的矩形区/// </summary>/// <param name="tabRect">标签页矩形区</param>/// <returns>按钮矩形区</returns>private Rectangle getCloseButtonRect(Rectangle tabRect){         if (Alignment == TabAlignment.Top | Alignment == TabAlignment.Bottom){tabRect.Offset(tabRect.Width - 21, tabRect.Height / 2 - 7);}else if (Alignment == TabAlignment.Left){tabRect.Offset(tabRect.Width / 2 - 8, 7);}else if (Alignment == TabAlignment.Right){tabRect.Offset(tabRect.Width / 2 - 7, tabRect.Height - 21);}tabRect.Height = 15;tabRect.Width = 15;return tabRect;}/// <summary>/// 绘制关闭按钮/// </summary>/// <param name="g"></param>/// <param name="tabRect">标签的框体</param>/// <param name="cusorPoint">鼠标在工作区的相对坐标值</param>private void DrawCloseButton(Graphics g, Rectangle tabRect, Point cusorPoint){Rectangle CBRect = getCloseButtonRect(tabRect);float i = 3.9F;float penWidth = 1.52F;Color lineColor = Color.FromArgb(200, 0, 0, 0);if (CBRect.Contains(cusorPoint)){using (SolidBrush brush = new SolidBrush(Color.FromArgb(170, 60, 0, 0))){g.FillEllipse(brush, CBRect);}lineColor = Color.FromArgb(200, 255, 255, 255);//此句作用为在绘制背景小圆的时候对关闭十字颜色重设penWidth = 1.5F;}using (Pen brush = new Pen(lineColor, penWidth)){g.DrawLine(brush, CBRect.X + i, CBRect.Y + i, CBRect.Right - i, CBRect.Bottom - i);g.DrawLine(brush, CBRect.X + i, CBRect.Bottom - i, CBRect.Right - i, CBRect.Top + i);}}/// <summary>/// 绘制标签Tip文字/// </summary>/// <param name="g">绘图面</param>/// <param name="tabRect">标签矩形框体</param>/// <param name="text">需要绘制的文字</param>private void DrawTipText(Graphics g, Rectangle tabRect,string text){           int num = text.Length,fontSize=7;if (Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(text)) != text){fontSize = 5;//判断字符串是否纯字母,存在中文则变字体大小num = num + 1;}Rectangle tipRect = getTipRect(tabRect, num);GraphicsPath tipPath = CreateTipPath(tipRect);using (SolidBrush brush = new SolidBrush(Color.FromArgb(150, _tipBackColor))){g.FillPath(brush, tipPath);}Font f = new System.Drawing.Font(this.Font.FontFamily, fontSize, this.Font.Style);tipRect.Offset(1, -1);TextRenderer.DrawText(g,text,f,tipRect,_tipTextColor);}/// <summary>/// 获取Tip绘制矩形框/// </summary>/// <param name="tabRect">标签框体</param>/// <param name="textLength">字体长度</param>/// <returns></returns>private Rectangle getTipRect(Rectangle tabRect,int textLength){if (Alignment == TabAlignment.Top | Alignment == TabAlignment.Bottom){tabRect.Offset(2, 1);tabRect.Width++;}else if (Alignment == TabAlignment.Left){tabRect.Offset(1,1);}else if (Alignment == TabAlignment.Right){tabRect.Offset(1, 1);}tabRect.Height = 10;tabRect.Width =4+ textLength*7;return tabRect;}/// <summary>/// 根据tiprect框体绘制有圆角的框体路径/// </summary>/// <param name="rect">tiprect</param>/// <returns></returns>private GraphicsPath CreateTipPath(Rectangle rect){GraphicsPath path = new GraphicsPath();path.AddArc(rect.X,rect.Y,rect.Height,rect.Height,90F,180F);path.AddLine(rect.X + rect.Height / 2,rect.Y,rect.Right - rect.Height / 2,rect.Y);path.AddArc(rect.Right - rect.Height,rect.Y,rect.Height,rect.Height,270F,180F);path.AddLine(rect.X + rect.Height / 2,rect.Bottom,rect.Right - rect.Height / 2,rect.Bottom);path.CloseFigure();return path;}/// <summary>/// 绘制标签的文字/// </summary>/// <param name="g">tabcontrol的graphics</param>/// <param name="page">标签页tabpage</param>/// <param name="tabRect">标签的框体</param>/// <param name="hasImage">是否绘制了图片</param>private void DrawtabText(Graphics g, TabPage page, Rectangle tabRect, bool hasImage){Rectangle textRect = tabRect;RectangleF newTextRect;StringFormat sf;Point padding = this.Padding;switch (Alignment){case TabAlignment.Top:case TabAlignment.Bottom:if (_haveCloseButton){if (hasImage){textRect.X = tabRect.X + Radius / 2 + tabRect.Height - padding.X+6;textRect.Width = tabRect.Width - Radius - tabRect.Height - 10;}else{textRect.X = tabRect.X +3- padding.X;}}else{if (hasImage){textRect.X = tabRect.X + Radius / 2 + tabRect.Height - 2;textRect.Width = tabRect.Width - Radius - tabRect.Height;}}TextRenderer.DrawText(g,page.Text,page.Font,textRect,page.ForeColor);break;case TabAlignment.Left:if (_haveCloseButton){if (hasImage){textRect.Height = tabRect.Height - tabRect.Width + padding.X+5;}else{textRect.Height = tabRect.Height + padding.X+5 ;}}else{if (hasImage){textRect.Height = tabRect.Height - tabRect.Width + 2;}}g.TranslateTransform(textRect.X, textRect.Bottom);g.RotateTransform(270F);sf = new StringFormat(StringFormatFlags.NoWrap);sf.Alignment = StringAlignment.Center;sf.LineAlignment = StringAlignment.Center;sf.Trimming = StringTrimming.Character;newTextRect = textRect;newTextRect.X = 0;newTextRect.Y = 0;newTextRect.Width = textRect.Height;newTextRect.Height = textRect.Width;using (Brush brush = new SolidBrush(page.ForeColor)){g.DrawString(page.Text,page.Font,brush,newTextRect,sf);}g.ResetTransform();break;case TabAlignment.Right:if (_haveCloseButton){if (hasImage){textRect.Y = tabRect.Y + Radius / 2 + tabRect.Width - padding.X;textRect.Height = tabRect.Height - Radius - tabRect.Width;}else{textRect.Y = tabRect.Y + Radius / 2+padding.X- 2;textRect.Height = tabRect.Height - Radius - tabRect.Width;}}else{if (hasImage){textRect.Y = tabRect.Y + Radius / 2 + tabRect.Width - 2;textRect.Height = tabRect.Height - Radius - tabRect.Width;}}g.TranslateTransform(textRect.Right, textRect.Y);g.RotateTransform(90F);sf = new StringFormat(StringFormatFlags.NoWrap);sf.Alignment = StringAlignment.Center;sf.LineAlignment = StringAlignment.Center;sf.Trimming = StringTrimming.Character;newTextRect = textRect;newTextRect.X = 0;newTextRect.Y = 0;newTextRect.Width = textRect.Height;newTextRect.Height = textRect.Width;using (Brush brush = new SolidBrush(page.ForeColor)){g.DrawString(page.Text,page.Font,brush,newTextRect,sf);}g.ResetTransform();break;}}/// <summary>/// 绘制框线/// </summary>/// <param name="g"></param>private void DrawBorder(Graphics g){if (SelectedIndex != -1){Rectangle tabRect = GetTabRect(SelectedIndex);Rectangle clipRect = ClientRectangle;Point[] points = new Point[6]; switch (Alignment){case TabAlignment.Top:points[0] = new Point(tabRect.X,tabRect.Bottom);points[1] = new Point(clipRect.X,tabRect.Bottom);points[2] = new Point(clipRect.X,clipRect.Bottom - 1);points[3] = new Point(clipRect.Right - 1,clipRect.Bottom - 1);points[4] = new Point(clipRect.Right - 1,tabRect.Bottom);points[5] = new Point(tabRect.Right,tabRect.Bottom);break;case TabAlignment.Bottom:points[0] = new Point(tabRect.X,tabRect.Y);points[1] = new Point(clipRect.X,tabRect.Y);points[2] = new Point(clipRect.X,clipRect.Y);points[3] = new Point(clipRect.Right - 1,clipRect.Y);points[4] = new Point(clipRect.Right - 1,tabRect.Y);points[5] = new Point(tabRect.Right,tabRect.Y);break;case TabAlignment.Left:points[0] = new Point(tabRect.Right,tabRect.Y);points[1] = new Point(tabRect.Right,clipRect.Y);points[2] = new Point(clipRect.Right - 1,clipRect.Y);points[3] = new Point(clipRect.Right - 1,clipRect.Bottom - 1);points[4] = new Point(tabRect.Right,clipRect.Bottom - 1);points[5] = new Point(tabRect.Right,tabRect.Bottom);break;case TabAlignment.Right:points[0] = new Point(tabRect.X,tabRect.Y);points[1] = new Point(tabRect.X,clipRect.Y);points[2] = new Point(clipRect.X,clipRect.Y);points[3] = new Point(clipRect.X,clipRect.Bottom - 1);points[4] = new Point(tabRect.X,clipRect.Bottom - 1);points[5] = new Point(tabRect.X,tabRect.Bottom);break;}using (Pen pen = new Pen(_borderColor)){g.DrawLines(pen, points);}}}/// <summary>/// 绘制标签背景/// </summary>/// <param name="g"></param>/// <param name="rect"></param>/// <param name="baseColor"></param>/// <param name="borderColor"></param>/// <param name="basePosition"></param>/// <param name="mode"></param>internal void RenderTabBackgroundInternal(Graphics g,Rectangle rect,Color baseColor,Color borderColor,float basePosition,LinearGradientMode mode){using (GraphicsPath path = CreateTabPath(rect)){using (LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, mode)){Color[] colors = new Color[4];colors[0] = GetColor(baseColor, 0, 35, 24, 9);colors[1] = GetColor(baseColor, 0, 13, 8, 3);colors[2] = baseColor;colors[3] = GetColor(baseColor, 0, 68, 69, 54);ColorBlend blend = new ColorBlend();blend.Positions =new float[] { 0.0f, basePosition, basePosition + 0.05f, 1.0f };blend.Colors = colors;brush.InterpolationColors = blend;g.FillPath(brush, path);}if (baseColor.A > 80){Rectangle rectTop = rect;if (mode == LinearGradientMode.Vertical){rectTop.Height = (int)(rectTop.Height * basePosition);}else{rectTop.Width = (int)(rect.Width * basePosition);}using (SolidBrush brushAlpha =new SolidBrush(Color.FromArgb(80, 255, 255, 255))){g.FillRectangle(brushAlpha, rectTop);}}rect.Inflate(-1, -1);using (GraphicsPath path1 = CreateTabPath(rect)){using (Pen pen = new Pen(Color.FromArgb(255, 255, 255))){if (Multiline){g.DrawPath(pen, path1);}else{g.DrawLines(pen, path1.PathPoints);}}}using (Pen pen = new Pen(borderColor)){if (Multiline){g.DrawPath(pen, path);}{g.DrawLines(pen, path.PathPoints);}}}}/// <summary>/// 绘制标签图标/// </summary>/// <param name="g"></param>/// <param name="page"></param>/// <param name="rect"></param>/// <returns></returns>private bool DrawTabImage(Graphics g, TabPage page, Rectangle rect){bool hasImage = false;if (ImageList != null){Image image = null;if (page.ImageIndex != -1){image = ImageList.Images[page.ImageIndex];}else if (page.ImageKey != null){image = ImageList.Images[page.ImageKey];}if (image != null){hasImage = true;Rectangle destRect = Rectangle.Empty;Rectangle srcRect = new Rectangle(Point.Empty, image.Size);switch (Alignment){case TabAlignment.Top:case TabAlignment.Bottom:destRect = new Rectangle(rect.X + Radius / 2 + 2,rect.Y + 2,rect.Height - 4,rect.Height - 4);break;case TabAlignment.Left:destRect = new Rectangle(rect.X + 2,rect.Bottom - (rect.Width - 4) - Radius / 2 - 2,rect.Width - 4,rect.Width - 4);break;case TabAlignment.Right:destRect = new Rectangle(rect.X + 2,rect.Y + Radius / 2 + 2,rect.Width - 4,rect.Width - 4);break;}g.DrawImage(image,destRect,srcRect,GraphicsUnit.Pixel);}}return hasImage;}/// <summary>/// 绘制tab标签的框体/// </summary>/// <param name="rect">tab框体矩形的位置和大小</param>/// <returns>返回一个绘制的框体</returns>private GraphicsPath CreateTabPath(Rectangle rect){GraphicsPath path = new GraphicsPath();switch (Alignment){case TabAlignment.Top:rect.X++;rect.Width-=2;path.AddLine(rect.X,rect.Bottom,rect.X,rect.Y + Radius / 2);path.AddArc(rect.X ,rect.Y,Radius,Radius,180F,90F);path.AddArc(rect.Right - Radius,rect.Y,Radius,Radius,270F,90F);path.AddLine(rect.Right,rect.Y + Radius / 2,rect.Right,rect.Bottom);break;case TabAlignment.Bottom:rect.X++;rect.Width-=2;path.AddLine(rect.X,rect.Y,rect.X,rect.Bottom - Radius / 2);path.AddArc(rect.X,rect.Bottom - Radius,Radius,Radius,180,-90);path.AddLine(rect.X+Radius/2,rect.Bottom,rect.Right-Radius / 2,rect.Bottom);path.AddArc(rect.Right - Radius,rect.Bottom - Radius,Radius,Radius,90,-90);path.AddLine(rect.Right,rect.Bottom - Radius / 2,rect.Right,rect.Y);break;case TabAlignment.Left:rect.Y++;rect.Height -= 2;path.AddLine(rect.Right,rect.Y,rect.X + Radius / 2,rect.Y);path.AddArc(rect.X,rect.Y,Radius,Radius,270F,-90F);path.AddArc(rect.X,rect.Bottom - Radius,Radius,Radius,180F,-90F);path.AddLine(rect.X + Radius / 2,rect.Bottom,rect.Right,rect.Bottom);break;case TabAlignment.Right:rect.Y++;rect.Height -= 2;path.AddLine(rect.X,rect.Y,rect.Right - Radius / 2,rect.Y);path.AddArc(rect.Right - Radius,rect.Y,Radius,Radius,270F,90F);path.AddArc(rect.Right - Radius,rect.Bottom - Radius,Radius,Radius,0F,90F);path.AddLine(rect.Right - Radius / 2,rect.Bottom,rect.X,rect.Bottom);break;}path.CloseFigure();return path;}/// <summary>/// 自定义颜色位偏移调整函数/// </summary>/// <param name="colorBase"></param>/// <param name="a"></param>/// <param name="r"></param>/// <param name="g"></param>/// <param name="b"></param>/// <returns></returns>private Color GetColor(Color colorBase, int a, int r, int g, int b){int a0 = colorBase.A;int r0 = colorBase.R;int g0 = colorBase.G;int b0 = colorBase.B;if (a + a0 > 255) { a = 255; } else { a = Math.Max(a + a0, 0); }if (r + r0 > 255) { r = 255; } else { r = Math.Max(r + r0, 0); }if (g + g0 > 255) { g = 255; } else { g = Math.Max(g + g0, 0); }if (b + b0 > 255) { b = 255; } else { b = Math.Max(b + b0, 0); }return Color.FromArgb(a, r, g, b);}/// <summary>/// 记录选项卡上次选中的标签Index编号。/// </summary>private void SelectIndexLog(){_lastSelectIndex = _selectIndex;_selectIndex = SelectedIndex;}/// <summary>/// 得到左右箭头绘制区域,以从标签绘制区域移除。防止标签绘图区与箭头绘图区重叠/// </summary>/// <param name="upDownHandle">左右箭头句柄</param>/// <returns></returns>private Rectangle upDownRect(IntPtr upDownHandle){Rectangle udRect = new Rectangle();if (upDownHandle != IntPtr.Zero){if (IsWindowVisible(upDownHandle)){RECT upDownRect = new RECT();GetClientRect(upDownHandle, ref upDownRect);udRect = Rectangle.FromLTRB(upDownRect.Left, upDownRect.Top, upDownRect.Right, upDownRect.Bottom);}}return udRect;}#endregion//以下代码功能在updownbutton类里面实现了//#region 得到左右箭头绘制区域,以从标签绘制区域移除。防止标签绘图区与箭头绘图区重叠#region 此段代码对win32API进行引用/// <summary>/// 矩形坐标块/// </summary>[StructLayout(LayoutKind.Sequential)]public struct RECT{internal RECT(int X, int Y, int Width, int Height){this.Left = X;this.Top = Y;this.Right = Width;this.Bottom = Height;}internal int Left;internal int Top;internal int Right;internal int Bottom;}/// <summary>/// 该函数获得指定窗口所属的类的类名/// </summary>/// <param name="hWnd">窗口的句柄及间接给出的窗口所属的类</param>/// <param name="ClassName">指向接收窗口类名字符串的缓冲区的指针</param>/// <param name="nMaxCount">指定由参数lpClassName指示的缓冲区的字节数。如果类名字符串大于缓冲区的长度,则多出的部分被截断</param>[DllImport("User32.dll", CharSet = CharSet.Auto)]public static extern int GetClassName(IntPtr hWnd, out char[] ClassName, int nMaxCount);[DllImport("user32.dll")]public static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter,string lpszClass,string lpszWindow);[DllImport("User32.dll")]private extern static IntPtr GetWindow(IntPtr hWnd, int wCmd);/// <summary>/// 通过该函数可以获得指定窗口的可视状态,即显示或者隐藏。/// </summary>/// <param name="hwnd"></param>/// <returns></returns>[DllImport("User32.dll", CharSet = CharSet.Auto)]public static extern bool IsWindowVisible(IntPtr hwnd);/// <summary>/// 该函数获取窗口客户区的坐标。客户区坐标指定客户区的左上角和右下角。由于客户区坐标是相对窗口客户区的左上角而言的,因此左上角坐标为(0,0)。/// </summary>/// <param name="hWnd">是程序窗口的句柄。</param>/// <param name="r">指向一个RECT类型的rectangle结构</param>/// <returns>如果函数成功,返回一个非零值。</returns>[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool GetClientRect(IntPtr hWnd, ref RECT r);const int GW_CHILD=5,GW_HWNDNEXT=2;# endregion#region  此段代码对按钮句柄进行分配和释放。/// <summary>/// 检查是否存在左右按钮,有则将bUpDown设置为true,同时将按钮句柄分配给ScUpdown。如果没有则将分配的的Scupdown释放。/// </summary>protected void checkUpDown(){FindUpDown();if (bUpDown == false){if (scUpDown != null){if (scUpDown.Handle != IntPtr.Zero) scUpDown.DestroyHandle();}}}/// <summary>/// 以下多个事件重写,目的都是在有可能按钮改变的情况下检查左右按钮是否存在/// </summary>protected override void OnCreateControl(){checkUpDown();base.OnCreateControl();}protected override void OnHandleCreated(EventArgs e){checkUpDown();base.OnHandleCreated(e);}protected override void OnSizeChanged(EventArgs e){checkUpDown();base.OnSizeChanged(e);}protected override void OnControlAdded(ControlEventArgs e){checkUpDown();base.OnControlAdded(e);}protected override void OnControlRemoved(ControlEventArgs e){checkUpDown();base.OnControlRemoved(e);}#endregionbool bUpDown = false;private NativeWindow scUpDown = null;private void FindUpDown(){bool bFound = false;IntPtr pWnd = FindWindowEx(this.Handle, IntPtr.Zero, "msctls_updown32", null);          if (pWnd != IntPtr.Zero){bFound = true;if (!bUpDown){scUpDown = new NativeWindow();this.scUpDown.AssignHandle(pWnd);bUpDown = true;}}if ((!bFound) && (bUpDown))bUpDown = false;          }private Rectangle upDownRect(){Rectangle udRect=new Rectangle();if (bUpDown){if (IsWindowVisible(scUpDown.Handle)){RECT upDownRect = new RECT();GetClientRect(scUpDown.Handle, ref upDownRect);udRect = Rectangle.FromLTRB(upDownRect.Left, upDownRect.Top, upDownRect.Right, upDownRect.Bottom);}}           return udRect;            }//#endregion}}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;namespace MyTabControl
{partial class MyTabControlEx{/// ///  此页代码解决左右箭头绘制问题/// #region 左右键绘制需要定义的一些参数private UpDownButtonNativeWindow _upDownButtonNativeWindow;private bool _drawUpDownButtonByCustom = true;private Color _arrowColor = Color.FromArgb(0, 79, 125);//箭头颜色/// <summary>/// 左右箭头颜色/// </summary>[Category("外观")][Description("控件左右箭头颜色")][DefaultValue(typeof(Color), "0, 95, 152")]public Color ArrowColor{get { return _arrowColor; }set{_arrowColor = value;base.Invalidate(true);}}/// <summary>/// 左右箭头是否自定义绘制/// </summary>[Browsable(true)][Category("外观")][Description("左右箭头是否由用户绘制,应对部分电脑同时选择\n呼吸灯效果和自绘箭头可能导致的闪烁问题")][EditorBrowsable(EditorBrowsableState.Always)][DefaultValue(typeof(bool), "true")]public bool DrawUpDownButtonByCustom{get { return _drawUpDownButtonByCustom; }set{_drawUpDownButtonByCustom = value;base.Invalidate(true);}}
#endregion#region 自定义左右键绘制事件private static readonly object EventPaintUpDownButton = new object();/// <summary>///第一个参数是一个键值,这样添加以后,可以通过下面的代码获取该事件base.Events[EventPaintUpDownButton] as EventHandler;/// </summary>public event UpDownButtonPaintEventHandler PaintUpDownButton{add { base.Events.AddHandler(EventPaintUpDownButton, value); }remove { base.Events.RemoveHandler(EventPaintUpDownButton, value); }}//定义一个EventHandler委托类型的事件属性,该事件可以用EventHandler类型的委托进行处理
#endregion#region 以下为win32API的引用public const int WM_PAINT = 0xF;public const int VK_LBUTTON = 0x1;public const int VK_RBUTTON = 0x2;private const int TCM_FIRST = 0x1300;public const int TCM_GETITEMRECT = (TCM_FIRST + 10);public static readonly IntPtr TRUE = new IntPtr(1);/// <summary>/// 该结构体包含了某应用程序用来绘制它所拥有的窗口客户区所需要的信息。/// </summary>[StructLayout(LayoutKind.Sequential)]public struct PAINTSTRUCT{internal IntPtr hdc;internal int fErase;internal RECT rcPaint;internal int fRestore;internal int fIncUpdate;internal int Reserved1;internal int Reserved2;internal int Reserved3;internal int Reserved4;internal int Reserved5;internal int Reserved6;internal int Reserved7;internal int Reserved8;}/// <summary>/// 矩形坐标块/// </summary>[StructLayout(LayoutKind.Sequential)]public struct RECT{internal RECT(int X, int Y, int Width, int Height){this.Left = X;this.Top = Y;this.Right = Width;this.Bottom = Height;}internal int Left;internal int Top;internal int Right;internal int Bottom;}[DllImport("user32.dll")]public static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter,string lpszClass,string lpszWindow);/// <summary>/// BeginPaint函数为指定窗口进行绘图工作的准备,并用将和绘图有关的信息填充到一个PAINTSTRUCT结构中。/// </summary>/// <param name="hWnd">[输入]被重绘的窗口句柄</param>/// <param name="ps">:[输出]指向一个用来接收绘画信息的PAINTSTRUCT结构</param>/// <returns>如果函数成功,返回值是指定窗口的“显示设备描述表”句柄。如果函数失败,返回值是NULL,表明没有得到显示设备的内容。</returns>[DllImport("user32.dll")]public static extern IntPtr BeginPaint(IntPtr hWnd, ref PAINTSTRUCT ps);/// <summary>/// EndPaint函数标记指定窗口的绘画过程结束;这个函数在每次调用BeginPaint函数之后被请求,但仅仅在绘画完成以后。/// </summary>/// <param name="hWnd">[输入]已经被重画的窗口的HANDLE</param>/// <param name="ps">[输入]指向一个PAINTSTRUCT结构,该结构包含了绘画信息,是BeginPaint函数返回的返回值</param>/// <returns>返回值始终是非0</returns>[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT ps);/// <summary>/// 该函数检取指定虚拟键的状态。该状态指定此键是UP状态,DOWN状态,还是被触发的(开关每次按下此键时进行切换)。/// </summary>/// <param name="nVirtKey">检查的虚拟键码</param>/// <returns>> 大于0 没按下,小于0被按下</returns>[DllImport("user32.dll")]public static extern short GetKeyState(int nVirtKey);/// <summary>/// /// </summary>/// <param name="hWnd"></param>/// <param name="Msg"></param>/// <param name="wParam"></param>/// <param name="lParam">传递rect类型矩形坐标</param>/// <returns></returns>[DllImport("user32.dll")]public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref RECT lParam);/// <summary>/// 该函数检取光标的位置,以屏幕坐标表示/// </summary>/// <param name="lpPoint"></param>/// <returns></returns>[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool GetCursorPos(ref Point lpPoint);/// <summary>/// 函数将指定的矩形移动到指定的位置,如果函数成功,返回非0,否则返回0./// </summary>/// <param name="lpRect">[输入输出]指向一个RECT结构,其中包含了被移动矩形的逻辑坐标</param>/// <param name="x">[输入]指定的矩形左右移动的量。当向左移动的时候,这个参数必须是一个负值</param>/// <param name="y">[输入]指定的矩形上下移动的量。当想上移动的时候,这个参数应该是一个负值</param>/// <returns></returns>[DllImport("user32.dll")]public extern static int OffsetRect(ref RECT lpRect, int x, int y);/// <summary>/// 判断一个点是否在Rect中/// </summary>/// <param name="lprc">一个指向RECT类型的常量指针,也就是说这个值是你要进行点是否在RECT对象的RECT类型的变量</param>/// <param name="pt">一个类型为POINT类型的变量,也就是你要进行判断点是否在RECT对象的点</param>/// <returns>如果点a在rect对象中,那么返回值为非零,否则返回值为0</returns>[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool PtInRect([In] ref RECT lprc, Point pt);/// <summary>/// 该函数返回指定窗口的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出。/// </summary>/// <param name="hWnd">窗口句柄</param>/// <param name="lpRect">指向一个RECT结构的指针,该结构接收窗口的左上角和右下角的屏幕坐标。</param>/// <returns>如果函数成功,返回值为非零:如果函数失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数</returns>[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);/// <summary>/// 该函数获取窗口客户区的坐标。客户区坐标指定客户区的左上角和右下角。由于客户区坐标是相对窗口客户区的左上角而言的,因此左上角坐标为(0,0)。/// </summary>/// <param name="hWnd">是程序窗口的句柄。</param>/// <param name="r">指向一个RECT类型的rectangle结构</param>/// <returns>如果函数成功,返回一个非零值。</returns>[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool GetClientRect(IntPtr hWnd, ref RECT r);/// <summary>/// 通过该函数可以获得指定窗口的可视状态,即显示或者隐藏。/// </summary>/// <param name="hwnd"></param>/// <returns></returns>[DllImport("User32.dll", CharSet = CharSet.Auto)]public static extern bool IsWindowVisible(IntPtr hwnd);#endregion#region 以下多个事件重写,目的是在需要绘制左右箭头时触发事件绘制箭头protected override void OnHandleCreated(EventArgs e){base.OnHandleCreated(e);checkUpDownButton();}/// <summary>/// 检查是否存在左右按钮,有则以句柄创建一个upDownButtonNativeWindow。如果没有则将分配的的内存释放。/// </summary>private void checkUpDownButton(){if (UpDownButtonHandle != IntPtr.Zero){if (_upDownButtonNativeWindow == null){_upDownButtonNativeWindow = new UpDownButtonNativeWindow(this);}}else {if (_upDownButtonNativeWindow != null){_upDownButtonNativeWindow.Dispose();_upDownButtonNativeWindow = null;}}}protected override void OnCreateControl(){base.OnCreateControl();checkUpDownButton();}protected override void OnHandleDestroyed(EventArgs e){base.OnHandleDestroyed(e);if (_upDownButtonNativeWindow != null){_upDownButtonNativeWindow.Dispose();_upDownButtonNativeWindow = null;}}protected override void OnControlAdded(ControlEventArgs e){base.OnControlAdded(e);checkUpDownButton();}protected override void OnSizeChanged(EventArgs e){base.OnSizeChanged(e);checkUpDownButton();}protected override void OnControlRemoved(ControlEventArgs e){base.OnControlRemoved(e);checkUpDownButton();}#endregion#region 左右键绘制方法/// <summary>/// 对左右键进行重绘/// </summary>/// <param name="e">左右键的参数,继承painteventargs</param>protected virtual void OnPaintUpDownButton(UpDownButtonPaintEventArgs e){Graphics g = e.Graphics;Rectangle rect = e.ClipRectangle;//程序分配给左右箭头的矩形Color upButtonBaseColor = _baseColor;Color upButtonBorderColor = _borderColor;Color upButtonArrowColor = _arrowColor;Color downButtonBaseColor = _baseColor;Color downButtonBorderColor = _borderColor;Color downButtonArrowColor = _arrowColor;Rectangle upButtonRect = rect;Bitmap bmp = new Bitmap(rect.Width, rect.Height);Graphics gb = Graphics.FromImage(bmp);Rectangle bmpRect = new Rectangle(0, 0, rect.Width, rect.Height);upButtonRect.X = 1;upButtonRect.Y = 1;upButtonRect.Width = rect.Width / 2 - 4;upButtonRect.Height -= 2;Rectangle downButtonRect = rect;downButtonRect.X = upButtonRect.Right + 2;downButtonRect.Y = 1;downButtonRect.Width = rect.Width / 2 - 4;downButtonRect.Height -= 2;if (Enabled){if (e.MouseOver){if (e.MousePress)//按下键时{if (e.MouseInUpButton){upButtonBaseColor = GetColor(_baseColor, 0, -35, -24, -9);}else{downButtonBaseColor = GetColor(_baseColor, 0, -35, -24, -9);}}else//仅仅是鼠标over未按键时{if (e.MouseInUpButton){upButtonBaseColor = GetColor(_baseColor, 0, 35, 24, 9);}else{downButtonBaseColor = GetColor(_baseColor, 0, 35, 24, 9);}}}}else{upButtonBaseColor = SystemColors.Control;upButtonBorderColor = SystemColors.ControlDark;upButtonArrowColor = SystemColors.ControlDark;downButtonBaseColor = SystemColors.Control;downButtonBorderColor = SystemColors.ControlDark;downButtonArrowColor = SystemColors.ControlDark;}g.SmoothingMode = SmoothingMode.AntiAlias;gb.SmoothingMode = SmoothingMode.AntiAlias;Color backColor = Enabled ? _backColor : SystemColors.Control;using (SolidBrush brush = new SolidBrush(_backColor)){rect.Inflate(1, 1);gb.FillRectangle(brush, bmpRect);}RenderButton(gb,upButtonRect,upButtonBaseColor,upButtonBorderColor,upButtonArrowColor,ArrowDirection.Left);RenderButton(gb,downButtonRect,downButtonBaseColor,downButtonBorderColor,downButtonArrowColor,ArrowDirection.Right);g.DrawImage(bmp, rect.X, rect.Y);bmp.Dispose();gb.Dispose();UpDownButtonPaintEventHandler handler =base.Events[EventPaintUpDownButton] as UpDownButtonPaintEventHandler;if (handler != null){handler(this, e);}}/// <summary>/// 左右箭头框体绘制/// </summary>/// <param name="g"></param>/// <param name="rect"></param>/// <param name="baseColor"></param>/// <param name="borderColor"></param>/// <param name="arrowColor"></param>/// <param name="direction"></param>internal void RenderButton(Graphics g,Rectangle rect,Color baseColor,Color borderColor,Color arrowColor,ArrowDirection direction){RenderBackgroundInternal(g,rect,baseColor,borderColor,0.45f,true,LinearGradientMode.Vertical);using (SolidBrush brush = new SolidBrush(arrowColor)){RenderArrowInternal(g,rect,direction,brush);}}/// <summary>/// 左右箭头绘制/// </summary>/// <param name="g"></param>/// <param name="dropDownRect"></param>/// <param name="direction"></param>/// <param name="brush"></param>internal void RenderArrowInternal(Graphics g,Rectangle dropDownRect,ArrowDirection direction,Brush brush){Point point = new Point(dropDownRect.Left + (dropDownRect.Width / 2),dropDownRect.Top + (dropDownRect.Height / 2));Point[] points = null;switch (direction){case ArrowDirection.Left:points = new Point[] { new Point(point.X + 1, point.Y - 4), new Point(point.X + 1, point.Y + 4), new Point(point.X - 2, point.Y) };break;case ArrowDirection.Up:points = new Point[] { new Point(point.X - 3, point.Y + 1), new Point(point.X + 3, point.Y + 1), new Point(point.X, point.Y - 1) };break;case ArrowDirection.Right:points = new Point[] {new Point(point.X - 1, point.Y - 4), new Point(point.X - 1, point.Y + 4), new Point(point.X + 2, point.Y) };break;default:points = new Point[] {new Point(point.X - 3, point.Y - 1), new Point(point.X + 3, point.Y - 1), new Point(point.X, point.Y + 1) };break;}g.FillPolygon(brush, points);}/// <summary>/// 绘制左右箭头背景/// </summary>/// <param name="g"></param>/// <param name="rect"></param>/// <param name="baseColor"></param>/// <param name="borderColor"></param>/// <param name="basePosition"></param>/// <param name="drawBorder"></param>/// <param name="mode"></param>internal void RenderBackgroundInternal(Graphics g,Rectangle rect,Color baseColor,Color borderColor,float basePosition,bool drawBorder,LinearGradientMode mode){using (LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, mode)){Color[] colors = new Color[4];colors[0] = GetColor(baseColor, 0, 35, 24, 9);colors[1] = GetColor(baseColor, 0, 13, 8, 3);colors[2] = baseColor;colors[3] = GetColor(baseColor, 0, 68, 69, 54);ColorBlend blend = new ColorBlend();blend.Positions =new float[] { 0.0f, basePosition, basePosition + 0.05f, 1.0f };blend.Colors = colors;brush.InterpolationColors = blend;g.FillRectangle(brush, rect);}if (baseColor.A > 80){Rectangle rectTop = rect;if (mode == LinearGradientMode.Vertical){rectTop.Height = (int)(rectTop.Height * basePosition);}else{rectTop.Width = (int)(rect.Width * basePosition);}using (SolidBrush brushAlpha =new SolidBrush(Color.FromArgb(80, 255, 255, 255))){g.FillRectangle(brushAlpha, rectTop);}}if (drawBorder){using (Pen pen = new Pen(borderColor)){g.DrawRectangle(pen, rect);}}}#endregion#region  自定义窗口类,将左右箭头句柄分配到此类窗口以进行绘图操作/// <summary>/// 得到左右箭头的句柄/// </summary>internal IntPtr UpDownButtonHandle{get { return FindUpDownButton(); }}private IntPtr FindUpDownButton()//多重嵌套时句柄有问题{return FindWindowEx(this.Handle,IntPtr.Zero,"msctls_updown32",null);}private class UpDownButtonNativeWindow : NativeWindow, IDisposable{private MyTabControlEx _owner;private bool _bPainting;public UpDownButtonNativeWindow(MyTabControlEx owner): base(){_owner = owner;base.AssignHandle(_owner.UpDownButtonHandle);}private bool LeftKeyPressed(){if (SystemInformation.MouseButtonsSwapped){return (GetKeyState(VK_RBUTTON) < 0);}else{return (GetKeyState(VK_LBUTTON) < 0);}}private void DrawUpDownButton(){bool mouseOver = false;bool mousePress = LeftKeyPressed();bool mouseInUpButton = false;RECT rect = new RECT();GetClientRect(base.Handle, ref rect);Rectangle clipRect = Rectangle.FromLTRB(rect.Top, rect.Left, rect.Right, rect.Bottom);Point cursorPoint = new Point();GetCursorPos(ref cursorPoint);GetWindowRect(base.Handle, ref rect);mouseOver = PtInRect(ref rect, cursorPoint);cursorPoint.X -= rect.Left;cursorPoint.Y -= rect.Top;mouseInUpButton = cursorPoint.X < clipRect.Width / 2;using (Graphics g = Graphics.FromHwnd(base.Handle)){UpDownButtonPaintEventArgs e =new UpDownButtonPaintEventArgs(g,clipRect,mouseOver,mousePress,mouseInUpButton);_owner.OnPaintUpDownButton(e);                    }}protected override void WndProc(ref Message m){switch (m.Msg){case  WM_PAINT:if (!_bPainting){PAINTSTRUCT ps =new  PAINTSTRUCT();_bPainting = true;BeginPaint(m.HWnd, ref ps);DrawUpDownButton();EndPaint(m.HWnd, ref ps);_bPainting = false;m.Result = TRUE;}else{base.WndProc(ref m);}break;default:base.WndProc(ref m);break;}}public void Dispose(){_owner = null;base.ReleaseHandle();}}#endregion}/// <summary>/// 自定义左右箭头绘制的事件委托/// </summary>/// <param name="sender"></param>/// <param name="e"></param>public delegate void UpDownButtonPaintEventHandler(object sender,UpDownButtonPaintEventArgs e);/// <summary>/// 继承PaintEventArgs的参数类,增加鼠标状态传递,over,press,updown等状态/// </summary>public class UpDownButtonPaintEventArgs : PaintEventArgs{private bool _mouseOver;private bool _mousePress;private bool _mouseInUpButton;public UpDownButtonPaintEventArgs(Graphics graphics,Rectangle clipRect,bool mouseOver,bool mousePress,bool mouseInUpButton): base(graphics, clipRect){_mouseOver = mouseOver;_mousePress = mousePress;_mouseInUpButton = mouseInUpButton;}public bool MouseOver{get { return _mouseOver; }}public bool MousePress{get { return _mousePress; }}public bool MouseInUpButton{get { return _mouseInUpButton; }}}
}

对tabcontrol控件增强,添加关闭按钮功能、呼吸灯标签闪烁功能、类QQ消息数量标签提示TIP相关推荐

  1. WPF 自定义TabControl控件样式(转)

    WPF 自定义TabControl控件样式 一.前言 程序中经常会用到TabControl控件,默认的控件样式很普通.而且样式或功能不一定符合我们的要求.比如:我们需要TabControl的标题能够居 ...

  2. 重新想象 Windows 8.1 Store Apps (79) - 控件增强: MediaElement, Frame

    重新想象 Windows 8.1 Store Apps (79) - 控件增强: MediaElement, Frame 原文:重新想象 Windows 8.1 Store Apps (79) - 控 ...

  3. C# WPF TabControl控件用法详解

    概述 TabControl我之前有讲过一节,内容详见:C# WPF TabControl用法指南(精品),上节主要讲解了tabcontrol控件的左右翻页,以及页面筛选,以及数据绑定等内容,这节内容继 ...

  4. Visual Studio中的TabControl控件的用法

    今天遇到了一个自己没遇到过的控件TabControl控件,所以找了点关于它的资料 TabControl 属性 DisplayRect:只定该控件客户区的一个矩形   HotTrack:设置当鼠标经过页 ...

  5. asp.net panel 加html,ASP.NET 页面中动态增加的控件、添加事件

    要求:页面上有一个Add按钮,每点击一次该按钮,页面上动态创建一个WebPartZone! 提醒:WebPartZone只能在OnInit或之前才能创建,否则报异常! 大家都知道,按钮的点击事件是在R ...

  6. TabControl控件和TabPage

    TabControl控件搞了两天才弄会,发个简单教程  TabControl控件可以支持在一个控件里面放置多个选项卡,每个选项卡又可以放置多个控件 由于在控件属性窗口添加选项卡相对比较容易,下面说一下 ...

  7. 在 GridView 控件中添加一列复选框51

    简介 在前面的教程中 , 我们学习了如何为 GridView 控件添加一列 单选 按钮来选择一个特定的记录.当用户被限制最多只能从网格中选中一项时,一列单选按钮是一个非常恰当的用户界面.然而,有时我们 ...

  8. TabControl控件用法图解

    1.首先创建一个MFC对话框框架,在对话框资源上从工具箱中添加上一个TabControl控件 2.根据需要修改一下属性,然后右击控件,为这个控件添加一个变量,将此控件跟一个CTabCtrl类变量绑定在 ...

  9. TabControl控件和TabPage的使用

    TabControl控件和TabPage     TabControl控件搞了两天才弄会,发个简单教程 TabControl控件可以支持在一个控件里面放置多个选项卡,每个选项卡又可以放置多个控件 由于 ...

最新文章

  1. 2022-2028年中国饮水机市场投资分析及前景预测报告
  2. 系统如何安装到租的服务器,租用服务器后,检查及环境配置步骤
  3. [Linux] rpm 与 dpkg 用法比较
  4. python中的map()函数详解
  5. [C++] - C++11 多线程 - Thread
  6. 28 岁裸辞转行程序员,一年的心路历程大曝光
  7. 【1138】数据结构上机测试2-1:单链表操作A(顺序建表+删除节点)(SDUT)
  8. 免费计算机网络基础ppt,计算机网络基础概念课件.ppt
  9. 美通企业日报 | 猫途鹰联手携程打造中国顶级旅行平台;强生战略合作阿里旗下Lazada...
  10. vs2015使用教程
  11. 百度色情图片识别API
  12. 编译java源文件(在cmd下编译)傻瓜式教学
  13. BI与SaaS碰撞,让数据处理更加轻松(下)
  14. python可视化进阶---seaborn1.3 分布数据可视化 - 直方图与密度图 displot() / kdeplot()/ rugplot()
  15. 前端开发工程师必备网站
  16. 位深度怎么调_吉他大神是怎么炼成的?
  17. java基础-常用快捷键及基本dos命令
  18. 由上海地铁10号线发生追尾事故所想到的
  19. adonis启动流程
  20. 计算机只有一块硬盘分区,如果新计算机只有一个分区怎么办?教您如何在不输入pe的情况下为硬盘创建新分区!...

热门文章

  1. SAP中公司与公司代码关系
  2. 当当电子书生成pdf示例
  3. 树莓派安装synology_如何从Synology NAS安装(和删除)应用程序包
  4. 网站邀请码php,简单的PHP邀请码系统
  5. 系列二 帶丄耳機全丗屆跟莪沒關係
  6. PPTV数据按照小类别分类
  7. A2P模式下的e租宝是否值得投资
  8. 小程序上格式化时间后 ios 真机显示 NaN
  9. S-属性定义与L-属性定义
  10. git commit 约定式提交