使用.net ,打造一款相似 Chrome 風格的 TabControl

國際慣例,先上圖:web

 

總體效果、操做風格等 與Chrome 保持高度接近,實現了標籤新增、刪除、移動、自適應寬度等特性。 

核心代碼,
1:建立Tab 頁邊框:ide

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:this

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;
            }
        }

 

使用方法:url

新增Tab頁,調用AddNewTab 方法,參數及方法源碼:spa

/// <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();
        }

 

 

時間倉促,先寫到這。後續有時間會繼續完善,增長功能以及優化性能 ,源碼下載地址:code

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

相關文章
相關標籤/搜索