65.9K
CodeProject 正在变化。 阅读更多。
Home

FireFox 式标签控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.61/5 (48投票s)

2007年8月16日

CPOL

3分钟阅读

viewsIcon

365522

downloadIcon

14064

一篇关于 Tab Control 的文章

Screenshot - image001.png

引言

最近我遇到了一个需求,需要从标签头本身关闭标签控件。这意味着用户不需要切换到任何选项卡页面就可以关闭它。一个完美的例子是 Firefox 浏览器。在 Firefox 中,用户可以打开任意数量的选项卡,并且可以随时关闭任何选项卡,而无需打开该选项卡。我尝试在 Google 上搜索解决方案,但没有找到完全符合我要求的内容。然后我想到了实现我自己的具有相同功能的选项卡控件。所以,这就是最终的控件。

Using the Code

该控件允许设计器像添加/删除普通选项卡控件一样添加/删除选项卡页面,并具有全新的外观。支持此功能的类是

  1. TabCtlEx.cs (继承自 System.Windows.Forms.TabControl
  2. TabPage.cs (继承自 System.Windows.Forms.TabPage

使用此控件非常简单,就像 .NET 选项卡控件一样。

private MyControlLibrary.TabCtlEx userControl11;
    TabPageEx tabPage1;
    TabPageEx tabPage2;
    TabPageEx tabPage3;

private TabPageEx tabPage4;
    this.userControl11 = new MyControlLibrary.TabCtlEx();
    this.tabPage1 = new MyControlLibrary.TabPageEx(this.components);
    this.tabPage2 = new MyControlLibrary.TabPageEx(this.components);
    this.tabPage3 = new MyControlLibrary.TabPageEx(this.components);
    this.tabPage4 = new MyControlLibrary.TabPageEx(this.components);
    this.Controls.Add(this. userControl11);

在每个选项卡头绘制关闭按钮

这需要您重写 .NET 选项卡控件中现有的 OnDrawItem() 函数。

/// <summary>
/// override to draw the close button
/// </summary>
/// <param name="e"></param>
protected override void OnDrawItem(DrawItemEventArgs e)
{
    RectangleF tabTextArea = RectangleF.Empty;
    for(int nIndex = 0 ; nIndex < this.TabCount ; nIndex++)
    {
        if( nIndex != this.SelectedIndex )
        {
            /*if not active draw ,inactive close button*/
            tabTextArea = (RectangleF)this.GetTabRect(nIndex);
            using(Bitmap bmp = new Bitmap(GetContentFromResource(
                "closeinactive.bmp")))
            {
                e.Graphics.DrawImage(bmp,
                    tabTextArea.X+tabTextArea.Width -16, 5, 13, 13);
            }
        }
        else
        {
            tabTextArea = (RectangleF)this.GetTabRect(nIndex);
            LinearGradientBrush br = new LinearGradientBrush(tabTextArea,
                SystemColors.ControlLightLight,SystemColors.Control,
                LinearGradientMode.Vertical);
            e.Graphics.FillRectangle(br,tabTextArea);

            /*if active draw ,inactive close button*/
            using(Bitmap bmp = new Bitmap(
                GetContentFromResource("close.bmp")))
            {
                e.Graphics.DrawImage(bmp,
                    tabTextArea.X+tabTextArea.Width -16, 5, 13, 13);
            }
            br.Dispose();
        }
        string str = this.TabPages[nIndex].Text;
        StringFormat stringFormat = new StringFormat();f
        stringFormat.Alignment = StringAlignment.Center; 
        using(SolidBrush brush = new SolidBrush(
            this.TabPages[nIndex].ForeColor))
        {
            /*Draw the tab header text
            e.Graphics.DrawString(str,this.Font, brush,
            tabTextArea,stringFormat);
        }
    }
}

在这里,关闭按钮实际上是绘制在每个选项卡头上的位图图像。因此,仅仅为了提供 Firefox 中使用的按钮的外观,使用了三个不同的位图图像

  • closeinactive.bmp
  • close.bmp
  • onhover.bmp

这些图像嵌入到控件中,并使用反射提取。还有一个用于获取嵌入图像资源的函数。此函数返回流,并通过将流传递给位图类来创建图像,如上所述。

/// <summary>
/// Get the stream of the embedded bitmap image
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
private Stream GetContentFromResource(string filename)
{
    Assembly asm = Assembly.GetExecutingAssembly();
    Stream stream =asm.GetManifestResourceStream(
        "MyControlLibrary."+filename);
    return stream;
}

Firefox 会询问用户是否要关闭选项卡页面。我希望我的控件也具有相同的功能,因为有时用户可能会意外按下关闭按钮,并且不想丢失所做的更改。这可以通过设置 Boolean 属性来实现。

private bool confirmOnClose = true;
public bool ConfirmOnClose
{
    get
    {
        return this.confirmOnClose;
    }
    set
    {
        this.confirmOnClose = value;
    }
}

属性

ConfirmOnClose 在关闭选项卡页面之前,会通过消息框进行确认。以下是检查单击区域是否在位图矩形内部的代码。

protected override void OnMouseDown(MouseEventArgs e)
{
    RectangleF tabTextArea = (RectangleF)this.GetTabRect(SelectedIndex);
    tabTextArea = 
        new RectangleF(tabTextArea.X+tabTextArea.Width -16,5,13,13);
    Point pt = new Point(e.X,e.Y);
    if(tabTextArea.Contains(pt))
    {
        if(confirmOnClose)
        {
            if(MessageBox.Show("You are about to close "+
                this.TabPages[SelectedIndex].Text.TrimEnd()+
                " tab. Are you sure you want to continue?","Confirm close",
                MessageBoxButtons.YesNo) == DialogResult.No)
            return;
        }
        //Fire Event to Client
        if(OnClose != null)
        {
            OnClose(this,new CloseEventArgs(SelectedIndex));
        }
    }
}

每当按下选项卡头上的关闭按钮时,控件将触发一个 OnClose() 事件给客户端,并将单击的选项卡索引作为参数。这将为客户端提供更大的灵活性,以便在关闭选项卡之前执行某些操作。

结论

这只是另一个提供一些灵活性和改进的用户界面的选项卡控件。我发现此功能在某些情况下非常有用。欢迎反馈!

历史

已更新菜单按钮功能。以上描述仅适用于添加关闭按钮,但文章源代码包含关闭按钮和菜单按钮的实际实现。此控件的主要优点是菜单项功能应用于每个选项卡页面,而不是选项卡控件。换句话说,我们可以为不同的选项卡页面设置不同的菜单项。

如果任何选项卡页面没有附加菜单,它将不会显示菜单按钮。下图显示 tabPage3 没有附加菜单,因此仅对该选项卡页面不显示菜单按钮。

Screenshot - image004.jpg

附加菜单项的实际代码是

this.tabPage1.Menu
    = contextMenuStrip1;
………
this.tabPage2.Menu
    = contextMenuStrip1;
………
this.tabPage3.Menu
    = contextMenuStrip1;
………
this.tabPage4.Menu
    = contextMenuStrip1;

您可以为每个选项卡页面分配相同的菜单项或不同的菜单项。

历史

  • 2007 年 8 月 16 日 -- 发布原始版本
  • 2007 年 8 月 28 日 -- 文章和下载已更新
  • 2008 年 6 月 24 日 -- 源代码已更新
© . All rights reserved.