WinForm打造一款类似 Chrome 风格的 TabControl

来源:互联网 发布:c语言经典例题100道 编辑:程序博客网 时间:2024/05/16 05:09

先上图:


 

整体效果、操作风格等 与Chrome 保持高度接近,实现了标签新增、删除、移动、自适应宽度等特性。 

核心代码,
1:创建Tab 页边框:

复制代码
public void drawRect(Graphics g, Rectangle rect) {            GraphicsPath path = new GraphicsPath();                        path = new GraphicsPath();            path.AddBezier(                new Point(rect.X, rect.Bottom),                new Point(rect.X + 3, rect.Bottom - 2),                new Point(rect.X + 3, rect.Bottom - 2),                new Point(rect.X + 4, rect.Bottom - 4));            //path.AddLine(rect.X + 4, rect.Bottom - 4, rect.Left + 15 - 4, rect.Y + 4);            path.AddBezier(                new Point(rect.Left + 15 - 4, rect.Y + 4),                new Point(rect.Left + 15 - 3, rect.Y + 2),                new Point(rect.Left + 15 - 3, rect.Y + 2),                new Point(rect.Left + 15, rect.Y));            //path.AddLine(rect.Left + 15, rect.Y, rect.Right - 15, rect.Y);            path.AddBezier(                new Point(rect.Right - 15, rect.Y),                new Point(rect.Right - 15 + 3, rect.Y + 2),                new Point(rect.Right - 15 + 3, rect.Y + 2),                new Point(rect.Right - 15 + 4, rect.Y + 4));            //path.AddLine(rect.Right - 15 + 4, rect.Y + 4, rect.Right - 4, rect.Bottom - 4);            path.AddBezier(                new Point(rect.Right - 4, rect.Bottom - 4),                new Point(rect.Right - 3, rect.Bottom - 3),                new Point(rect.Right - 3, rect.Bottom - 3),                new Point(rect.Right, rect.Bottom));            region = new System.Drawing.Region(path);            g.DrawPath(new Pen(Color.Black), path);            g.FillPath(new SolidBrush(Selected ? Color.White : noSelectedColor), path);            g.DrawLine(new Pen(Selected ? Color.White : BottomLineColor, 1), rect.X + 2, rect.Bottom - 1, rect.Right - 2, rect.Bottom - 1); }
复制代码

 

2:绘制图标, 标签的图标有两种,一种为静态,一种为动态图,比如当状态为Loading 时,则显示动态图,之前考虑过使用GIF,效果不太理想,所以改为Timer 的方式,循环显示一组图片,实现GIF的效果

复制代码
public void drawTabIcon(Graphics g, Rectangle rect){            if (webPageState == WebPageState.Loading)            {                if(iCurFrame == 0)                    g.DrawImage((Image)Resources.ResourceManager.GetObject("Marty_000" + (0).ToString("00")), rect);                else                    g.DrawImage((Image)Resources.ResourceManager.GetObject("Marty_000" + (iCurFrame - 1).ToString("00")), rect);            }            else                g.DrawImage(HeaderIcon, rect); }void tmAnimation_Tick(object sender, EventArgs e){            iCurFrame = (iCurFrame) % iFrameCount + 1;            this.paintType = PaintType.PaintHeaerIcon;            paintRequest();}
复制代码

3:绘制Tab 的文本, 仔细看Chrome 的实现,就会发现当文字超出Tab宽度时, 接近Tab边缘的区域,文字颜色会逐级变淡,这里也使用线性画刷实现了该效果:

复制代码
public void drawString(Graphics g, Rectangle rect, Rectangle rectFontLinearBrush, string title, Font font) {            g.DrawString(title, font, brushFont, rect);            using (LinearGradientBrush brush = new LinearGradientBrush(rectFontLinearBrush, Color.Transparent, Selected ? Color.White : noSelectedColor, 0, false))            {                g.FillRectangle(brush, rectFontLinearBrush);            }}
复制代码

 

4:实现Tab页顺序调整:

复制代码
protected override void OnMouseMove(MouseEventArgs e)        {            base.OnMouseMove(e);            if (e.Button == System.Windows.Forms.MouseButtons.Left && thMouseDown != null)            {                if (!slided)                {                    if (Math.Abs(e.X - pMouseDown.X) > 15)                    {                        slided = true;                    }                }                else                {                    btnAddNew.Visible = false;                    Point newPos = thMouseDown.Rect.Location;                    newPos.X += e.Location.X - pMouseDown.X;                    // 是否在父窗体范围内移动                    if (newPos.X < 0)                        newPos.X = 0;                    if (newPos.X > this.Width - thMouseDown.Rect.Width)                        newPos.X = this.Width - thMouseDown.Rect.Width;                                       // 判断移动方向,向左或向右                    if (e.Location.X - pMouseDown.X > 0)                    {                        // 判断是否已经是最后一个Tab                        if (thMouseDown.TabIndex != lstTabHeader.Count - 1)                        {                            TabHeader thRight = lstTabHeader[thMouseDown.TabIndex + 1];                            // 向右移动时,判断是否与后一Tab 交换位置:当前Tab的 Right ,超过后一Tab 位置的一半                            if (newPos.X + tabWidth > thRight.Rect.X + tabWidth / 2)                            {                                thRight.TabIndex --;                                thMouseDown.TabIndex ++;                                lstTabHeader.Sort();                            }                        }                    }                    else                    {                        // 判断是否已经是第0个Tab                        if (thMouseDown.TabIndex != 0)                        {                            TabHeader thLeft = lstTabHeader[thMouseDown.TabIndex - 1];                            // 向右移动时,判断是否与后一Tab 交换位置:当前Tab的 Right ,超过后一Tab 位置的一半                            if (newPos.X < thLeft.Rect.X + tabWidth / 2)                            {                                thLeft.TabIndex ++;                                thMouseDown.TabIndex --;                                lstTabHeader.Sort();                            }                        }                    }                    thMouseDown.Rect.X = newPos.X;                    pMouseDown = e.Location;                    this.Invalidate();                }            }            else            {                this.Invalidate();            }        }
复制代码

 

5:重载Control 的 OnPaint:

复制代码
protected override void OnPaint(PaintEventArgs e)        {            base.OnPaint(e);            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;            e.Graphics.CompositingQuality = CompositingQuality.HighQuality;            e.Graphics.DrawLine(new Pen(TabHeader.BottomLineColor), new Point(0, this.Bottom - 1), new Point(this.Width, this.Bottom - 1));            // 判断重绘区域大小,解决由最小化还原后,无法绘制Tab的问题            if (currPaintTh == null || e.ClipRectangle.Size.Width > TabHeader.Left_Offset)            {                // 被选中的Tab 需要处于顶层,因此最后绘制                TabHeader thSelected = null;                foreach (TabHeader th in lstTabHeader)                {                    if (th.Selected)                        thSelected = th;                    else                        th.DrawAll(e.Graphics, th.Rect);                }                // 最后绘制                if (thSelected != null)                    thSelected.DrawAll(e.Graphics, thSelected.Rect);            }            else            {                // 绘制完整的TabHeader,如果仅绘制指定区域,可能会出现白色背景                currPaintTh.DrawAll(e.Graphics, currPaintTh.Rect);                currPaintTh = null;            }        }
复制代码

 

使用方法:

新增Tab页,调用AddNewTab 方法,参数及方法源码:

复制代码
/// <summary>        /// 新增Tab        /// </summary>        /// <param name="title">标题</param>        /// <param name="font">字体</param>        /// <param name="url">网址</param>        public void AddNewTab(string title, Font font, string url = "")        {            widthCalculate();            TabHeader newTh = new TabHeader(lstTabHeader.Count, title, font, tabWidth, this, url);            newTh.Selected = true;            foreach (TabHeader th in lstTabHeader)            {                th.Selected = false;            }                        lstTabHeader.Add(newTh);            newTh.OnPaintRequest += newTh_OnPaintRequest;            this.Invalidate();        }
复制代码

 

 

时间仓促,先写到这。后续有时间会继续完善,增加功能以及优化性能 ,源码下载地址:

http://files.cnblogs.com/files/perpetual/BroswerX.zip

2 0
原创粉丝点击