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

TdhTabCtl - Firefox 式子类化 TabControl 和 TabPage

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (9投票s)

2008年7月29日

CPOL

13分钟阅读

viewsIcon

106707

downloadIcon

3796

本文介绍了具有类似 Firefox 的选项卡按钮和其他增强功能的 .NET 子类化 TabControl 和 TabPage 控件。

简介和功能

我之前提交给 The Code Project 的一篇文章(TdhEditBox)源于本文介绍的项目:TDHTabCtl,其中包含子类化控件 TdhTabCtl(继承自 System.Windows.Forms.TabControl)和 TdhTabPage(继承自 System.Windows.Forms.TabPage)。

TDHTabCtl 项目基于 "vijayaprasen" 最初编写的有趣的子类化 TabControlTabPage 控件(参见:“类似 Firefox 的 Tab Control”),通过研究这些代码我学到了很多。当我开始进行这个项目时,我原本只打算将 "vijayaprasen" 的 .NET 2.0 代码转换为 .NET 1.1 代码,然后做一个相对简单但重要的更改(即向项目中添加一个子类化的 TabPageCollection 类)。结果,我最终完全重写了原始项目,并添加了使我的版本与原始版本不同的功能。

TDHControls.TDHTabCtl.TdhTabCtlTDHControls.TDHTabCtl.TdhTabPage 控件的一些功能是:

TdhTabCtl

  • 允许或禁止关闭任何 TdhTabPage。此操作会触发一个事件。
  • 在关闭任何 TdhTabPage 之前需要或不需要确认。
  • 允许或禁止打开新的 TdhTabPage。此操作会触发一个事件。
  • 允许或禁止“重命名”(更改选项卡上的文本)任何 TdhTabPage。此操作会触发一个事件。
  • 允许或禁止重新排序 TdhTabPage。此操作会触发一个事件。
  • 允许或禁止在任何 TdhTabPage 的选项卡/标题上进行右键单击上下文菜单。

TdhTabPage

  • 允许或禁止关闭单个 TdhTabPage。此操作会触发一个事件。
  • 在关闭单个 TdhTabPage 之前需要或不需要确认。
  • 允许或禁止在单个 TdhTabPage 上进行右键单击上下文菜单。
  • 在单个 TdhTabPage 上显示或不显示“关闭”按钮。
  • 在单个 TdhTabPage 上显示或不显示“菜单”按钮。
  • 默认菜单不必单独分配给每个 TdhTabPage
  • (可选)分配给单个 TdhTabPage 的上下文菜单将在显示该 TdhTabPage 的菜单时合并到默认菜单中。

事件

TdhTabCtlTdhTabPage 控件会触发事件,以通知客户端代码何时执行了这些“增强”功能(添加、删除、重命名、重新排序)(例如,当用户关闭 TdhTabPage 时)。

自定义上下文菜单

任何 TdhTabPage 控件都可以选择性地分配一个唯一的上下文菜单。在演示截图的“Page3”上可以看到分配了一个这样的唯一/单独的上下文菜单。

在应用程序中使用 TdhTabCtl 和 TdhTabPage 控件

TDHTabCtl 项目是使用 VS2002 (.NET 1.0) 编写(和编译)的,目的是让其他开发者无论使用什么 .NET 版本都能方便地访问源代码。

要按原样使用 TdhTabCtlTdhTabPage 类,请在您的项目中添加对类库 'TDHTabCtl.dll' 的引用。

// The namespace used in the 'TDHTabCtl.dll' library is:
using TDHControls.TDHTabCtl;

// Sample code using the TdhTabCtl and TdhTabPage controls:
private void InitializeComponent()
{
    // 
    // tdhTabCtl1
    // 
    this.tdhTabCtl1.Anchor = ((System.Windows.Forms.AnchorStyles.Top 
        | System.Windows.Forms.AnchorStyles.Left) 
        | System.Windows.Forms.AnchorStyles.Right);
    this.tdhTabCtl1.Controls.AddRange(
        new System.Windows.Forms.Control[] 
        {
            this.tdhTabPage1, 
            this.tdhTabPage2, 
            this.tdhTabPage3, 
            this.tdhTabPage4
        });
    this.tdhTabCtl1.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
    this.tdhTabCtl1.ItemSize = new System.Drawing.Size(230, 24);
    this.tdhTabCtl1.Name = "tdhTabCtl1";
    this.tdhTabCtl1.SelectedIndex = 0;
    this.tdhTabCtl1.Size = new System.Drawing.Size(800, 216);
    this.tdhTabCtl1.TabIndex = 2;
    this.tdhTabCtl1.TabStop = false;
    // optionally assign EventHandler
    this.tdhTabCtl1.OnTabEvents 
        += new TDHControls.TDHTabCtl.TabEventsDelegate(this.tdhTabCtl1_OnTabEvents);
    // optionally assign EventHandler
    this.tdhTabCtl1.OnTabsReordered += new 
      TDHControls.TDHTabCtl.TabsReorderedEventDelegate(this.tdhTabCtl1_OnTabsReordered);
}

private void tdhTabCtl1_OnTabEvents(object sender, TDHControls.TDHTabCtl.TabEventArgs e)
{
    switch (e.TabEvent)
    {
        case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabAdded:
            // optionally do something
            break;
        case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabAddRejected:
            // optionally do something

            // For instance:
            // Add the [TdhTabPage] to the TabPageCollection of a standard TabControl
            //this.tabControl1.Controls.Add(e.TabPage);
            this.tabControl1.TabPages.Add(e.TabPage);
            break;
        case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabRemoved:
            // optionally do something

            // For instance:
            // Add the [TdhTabPage] to the TabPageCollection of a standard TabControl
            //this.tabControl1.Controls.Add(e.TabPage);
            this.tabControl1.TabPages.Add(e.TabPage);
            break;
        case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabRenamed:
            // optionally do something
            break;
        case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabsReordered:
            // This "subevent" is not raised 
            // if the [tdhTabCtl1.OnTabsReordered] eventhandler is assigned
            // It is raised for each TdhTabPage affected by the reorder

            // optionally do something

            Console.WriteLine("TdhTabPage reordered."      // TEST
                +"    OldInd="+ e.TabIndexOld.ToString()   // TEST
                +"    NewInd="+ e.TabIndexNew.ToString()); // TEST
            break;
        default:
            break;
    }
}

private void tdhTabCtl1_OnTabsReordered(object sender, 
        TDHControls.TDHTabCtl.TabsReorderedEventArgs e)
{
    // do something

    // For instance:
    for (int idx = 0; idx < e.TabOrder_int.Length; idx++)
    {
        Console.WriteLine("TdhTabPage reordered."          // TEST
            +"    OldInd="+ e.TabOrder_int[idx].ToString() // TEST
            +"    NewInd="+ idx.ToString() );              // TEST
    }
}

TdhTabCtl 和 TdhTabPage 的最终用户体验

由于 TdhTabCtlTdhTabPage 控件继承自标准的 System.Windows.Forms.TabControlSystem.Windows.Forms.TabPage 控件,因此可以预期最终用户会轻松理解它们。新增的功能应该是自 explanatory 的。

“重命名” TdhTabPage 控件

启用此功能后,可以通过 TdhTabCtl 控件的内置上下文菜单访问,允许最终用户更改 TdhTabPage 控件上显示的 .Text 值。

“重命名” TdhTabPage 控件的功能的目的是使用户与之的交互直观且轻松。因此,无论是接受还是拒绝重命名操作,都不需要特殊的用户操作或辅助控件来关闭编辑框。相反,编辑框可以通过键盘或鼠标行为关闭。

具体来说

  • 单击编辑框外部的任何位置都会关闭它并拒绝该操作。
  • 使用 [Escape] 键关闭编辑框并拒绝该操作。
  • 使用 [Return] 键关闭编辑框并接受该操作。
  • 使用 [Tab] 键关闭编辑框并接受该操作。

重新排序 TdhTabCtl 的 TdhTabPages

启用此功能后,可以通过 TdhTabCtl 控件的内置上下文菜单访问。以下截图显示了此功能的用法。

Sample Image

重新排序 TdhTabCtlTdhTabPage 集合并不像简单的拖放操作那样直接。另一方面,此操作的代码允许最终用户预览最终结果并接受或拒绝该操作。

TdhTabCtl 和 TdhTabPage 编程接口

TdhTabCtl 控件接口的新颖属性和方法(除了继承的成员)是:

  • public TDHTabCtl.TdhTabPageCollection TdhTabPages - 此属性扩展了标准的 .TabPages 属性;它了解 System.Windows.Forms.TabPageTDHTabCtl.TdhTabPage 类,并提供附加方法和属性(例如,'Insert()' 方法)。
  • public new System.Windows.Forms.TabControl.TabPageCollection TabPages - 这是 System.Windows.Forms.TabControl 的标准 .TabPages 属性;它只了解 System.Windows.Forms.TabPage 类。
  • 以下属性用于任何不是 TDHTabCtl.TdhTabPage(或其继承类)的 Tab 对象。它们对应于 TDHTabCtl.TdhTabPage 类的某些新颖属性。由于标准的 System.Windows.Forms.TabPage 控件确实具有这些属性,因此 TDHTabCtl.TdhTabCtl 类在设置值时会使用对于任何非 TDHTabCtl.TdhTabPage 的 Tab 对象。

  • public bool TabsBase_AllowClose - 此属性指示是否可以删除/关闭 Tab 对象。
  • public bool TabsBase_ConfirmOnClose - 此属性指示在删除/关闭 Tab 对象之前是否需要确认。
  • public bool TabsBase_ContextMenuAllowOpen - 此属性指示 TDHTabCtl.TdhTabCtl 控件的内置上下文菜单中的“打开新选项卡(基本 TabPage)”MenuItem 是否已启用/可用。


  • public bool TabsAllowClose - 此属性指示 TdhTabCtl.TdhTabCtl 控件的 .TabPageCollection 中的任何 Tab 对象是否可以被删除/关闭。
  • public bool TabsAllowContextMenu - 此属性指示 TdhTabCtl.TdhTabCtl 控件的内置上下文菜单是否对控件的 .TabPageCollection 中的任何 Tab 对象启用/可用。
  • public bool TabsConfirmOnClose - 此属性指示在删除/关闭 TdhTabCtl.TdhTabCtl 控件的 .TabPageCollection 中的任何 Tab 对象之前是否通常需要确认。
  • public bool TabsContextMenuAllowClose - 此属性指示 TdhTabCtl.TdhTabCtl 控件的内置上下文菜单中的“关闭选项卡”MenuItem 是否已启用/可用。
  • public bool TabsContextMenuAllowOpen - 此属性指示 TdhTabCtl.TdhTabCtl 控件的内置上下文菜单中的“打开新选项卡”MenuItem 是否已启用/可用。
  • public bool TabsContextMenuAllowRename - 此属性指示 TdhTabCtl.TdhTabCtl 控件的内置上下文菜单中的“重命名选项卡”MenuItem 是否已启用/可用。
  • public bool TabsContextMenuAllowReorder - 此属性指示 TdhTabCtl.TdhTabCtl 控件的内置上下文菜单中的“重新排序选项卡页面”MenuItem 是否已启用/可用。
  • public bool TabsShowCloseButton - 此属性指示是否可以在 TdhTabCtl.TdhTabCtl 控件的 .TabPageCollection 中的任何 TDHTabCtl.TdhTabPage 对象上绘制/显示“关闭按钮”。
  • public bool TabsShowMenuButton - 此属性指示是否可以在 TdhTabCtl.TdhTabCtl 控件的 .TabPageCollection 中的任何 TDHTabCtl.TdhTabPage 对象上绘制/显示“菜单按钮”。
    与以下所有方法一起
    1. 如果 OnTabEvents EventHandler 已附加到 TDHTabCtl.TdhTabCtl 控件,则事件将触发,如同用户手动删除/关闭 Tab 对象一样。
    2. 参数 'overridePermission' 的“true”值将覆盖用户尝试手动删除/关闭 Tab 对象时通常适用于给定对象的 .TabsAllowClose、.TabsBase_AllowClose 或 .TabAllowClose
    3. 返回值指示是否在 .TabPageCollection 中找到给定对象并将其删除,或者给定的索引值是否有效且具有这些值的对象已被删除。
  • public bool TabPages_Remove(bool overridePermission, TDHTabCtl.TdhTabPage theTabPage) - 此方法从 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中删除给定的 TDHTabCtl.TdhTabPage 对象。
  • public bool TabPages_Remove(bool overridePermission, System.Windows.Forms.TabPage theTabPage) - 此方法从 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中删除给定的(装箱的)System.Windows.Forms.TabPage 对象。
  • public bool TabPages_RemoveAt(bool overridePermission, int index) - 此方法从 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中删除具有给定索引值的 Tab 对象。
  • public bool TabPages_RemoveRange(bool overridePermission, int indexStart, int indexEnd) - 此方法从 TdhTabCtl.TdhTabCtl 控件的 .TabPageCollection 中删除具有给定索引值范围的 Tab 对象。

TdhTabPage 控件的新颖属性(除了继承的成员)是:

  • public bool TabAllowClose - 此属性指示是否可以删除/关闭特定的 TDHTabCtl.TdhTabPage 控件。
  • public bool TabAllowContextMenu - 此属性指示 TdhTabCtl.TdhTabCtl 控件的内置上下文菜单是否对特定的 TdhTabPage 控件启用/可用。
  • public bool TabConfirmOnClose - 此属性指示在删除/关闭特定的 TDHTabCtl.TdhTabPage 控件之前是否需要确认。
  • public bool TabShowCloseButton - 此属性指示是否应在特定的 TDHTabCtl.TdhTabPage 控件上绘制/显示“关闭按钮”。
  • public bool TabShowMenuButton - 此属性指示是否应在特定的 TDHTabCtl.TdhTabPage 控件上绘制/显示“菜单按钮”。
TDHTabCtl.TdhTabCtl.TdhTabPages 属性的新颖属性和方法(除了继承的成员)是:
  • public new TDHTabCtl.TdhTabPage this[int index] - 这是 TDHTabCtl.TdhTabCtl.TdhTabPages 属性的主索引器。如果父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中索引处的 Tab 对象不是 TDHTabCtl.TdhTabPage(或其继承类)的实例,则返回的对象是 null 对象——因此,如果您的项目允许将 TdhTabCtl.TdhTabPageSystem.Windows.Forms.TabPage 添加到 TdhTabCtl.TdhTabCtl,则最好使用下一个索引器。示例用法:[ this.tdhTabCtl1.TabPages[0].Text = "A TdhTabPage"; ]
  • public System.Windows.Forms.TabPage this[bool alwaysAsBase, int index] - 此索引器将父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中索引处的 Tab 对象(装箱后)作为标准 System.Windows.Forms.TabPage 返回。布尔值 'alwaysAsBase' 在代码中未使用;它仅用于区分签名。示例用法:[ this.tdhTabCtl1.TabPages[true, 0].Text = "A TdhTabPage"; ]
  • public TDHTabCtl.TdhTabPage this[string text] - 此索引器尝试返回父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection.Text 值与 'text;' 匹配的 TDHTabCtl.TdhTabPage 对象;否则返回 null 对象。
  • public System.Windows.Forms.TabPage this[bool alwaysAsBase, string text] - 此索引器尝试返回父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection.Text 值与 'text;' 匹配的(装箱的)System.Windows.Forms.TabPage 对象;否则返回 null 对象。
  • public bool IsTdhTabPage(int index) - 此方法指示父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中索引处的 Tab 对象是否为 TDHTabCtl.TdhTabPage(或其继承类)。
  • public new void Clear() - 此方法直接清空父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public bool Contains(TDHTabCtl.TdhTabPage theTabPage) - 此方法指示父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 是否包含给定的 TDHTabCtl.TdhTabPage 对象。
  • public new bool Contains(System.Windows.Forms.TabPage theTabPage) - 此方法指示父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 是否包含给定的 System.Windows.Forms.TabPage 对象。
  • public void Add(TDHTabCtl.TdhTabPage theTabPage) - 此方法将给定的 TDHTabCtl.TdhTabPage 对象添加到父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void Add(System.Windows.Forms.TabPage theTabPage) - 此方法将给定的 System.Windows.Forms.TabPage 对象添加到父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void AddRange(params TDHTabCtl.TdhTabPage[] theTabPages) - 此方法将给定的 TDHTabCtl.TdhTabPage 对象数组添加到父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void AddRange(params System.Windows.Forms.TabPage[] theTabPages) - 此方法将给定的 System.Windows.Forms.TabPage 对象数组添加到父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void Insert(int index, TDHTabCtl.TdhTabPage theTabPage) - 此方法将给定的 TDHTabCtl.TdhTabPage 对象插入父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void Insert(int index, System.Windows.Forms.TabPage theTabPage) - 此方法将给定的 System.Windows.Forms.TabPage 对象插入父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void InsertRange(int index, params TDHTabCtl.TdhTabPage[] theTabPages) - 此方法将给定的 TDHTabCtl.TdhTabPage 对象数组插入父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public void InsertRange(int index, params System.Windows.Forms.TabPage[] theTabPages) - 此方法将给定的 System.Windows.Forms.TabPage 对象数组插入父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection
  • public bool RemoveRange(int indexStart, int indexEnd) - 此方法删除父 TDHTabCtl.TdhTabCtl 控件的 .TabPageCollection 中索引值在 'indexStart' 和 'indexEnd'(含)之间的 Tab 对象。

TdhTabCtl 控件的事件处理程序和委托

  • public event TDHControls.TDHTabCtl.TabEventsDelegate OnTabEvents - 当在 TdhTabCtl 控件的 TdhTabPage 集合上执行了各种操作时,将触发 OnTabEvents 事件:
    • 添加 TdhTabPage
    • 开始但然后拒绝添加 TdhTabPage
    • 删除/关闭 TdhTabPage
    • “重命名” TdhTabPage
    • 重新排序 TdhTabPage 集合
    • (注意:如果为 TdhTabCtl 控件分配了 OnTabsReordered 事件处理程序,则 OnTabEvents 事件不会为该操作触发;否则,它会为受影响的每个 TdhTabPage 单独触发。)

    TabEventArgs 参数的各种属性提供有关事件的特定信息,包括对受影响的 TdhTabPage 实例的引用。

  • public delegate void TabEventsDelegate(object sender, TDHControls.TDHTabCtl.TabEventArgs e) - 此事件委托定义了 OnTabEvents 事件处理程序的签名。
  • public event TDHControls.TDHTabCtl.TabsReorderedEventDelegate OnTabsReordered - 当 TdhTabCtl 控件的 TdhTabPage 集合被重新排序时,将触发 OnTabsReordered 事件。TabsReorderedEventArgs 参数的属性中包含一个整数值列表(也可作为整数数组访问),可用于确定与旧的标签顺序相比,新的标签顺序。
  • public delegate void TabsReorderedEventDelegate(object sender, TDHControls.TDHTabCtl.TabsReorderedEventArgs e) - 此事件委托定义了 OnTabsReordered 事件处理程序的签名。

历史

  • 2008 年 7 月 29 日:将 TdhTabCtl ver 1.0.004 提交到 The Code Project。
  • 2008 年 8 月 1 日:TdhTabCtl ver 1.0.005
    • 修复了明显的时序问题。当最后一个(最右边的)TdhTabPage 通过 ContextMenu 被删除/关闭时,它的 TabRect 在删除后又被重新绘制。
  • 2008 年 8 月 8 日:TdhTabCtl ver 1.0.010 - 1.0.012
    使 TdhTabCtl 控件能够包含和处理 TdhTabPage.Forms.TabPage 控件。
    • 将重写的 TdhTabCtl.TabPages 属性(即 TDHControls.TDHTabCtl.TdhTabPageCollection 类实例的访问器)重命名为 TdhTabCtl.TdhTabPages
    • 因此,TdhTabCtl 实例的 .TabControl.TabPages 属性返回 base.TabPages
    TDHControls.TDHTabCtl.TdhTabCtl 类添加了以下属性和方法:
    • public bool TabsBase_AllowClose
    • public bool TabsBase_ConfirmOnClose
    • public bool TabsBase_ContextMenuAllowOpen
    • TabPages_Remove(bool overridePermission, TDHTabCtl.TdhTabPage theTabPage)
    • TabPages_Remove(bool overridePermission, System.Windows.Forms.TabPage theTabPage)
    • public bool TabPages_RemoveAt(bool overridePermission, int index)
    • public bool TabPages_RemoveRange(bool overridePermission, int indexStart, int indexEnd)
    TDHControls.TDHTabCtl.TdhTabPageCollection 类(通过 TdhTabCtl.TdhTabPages 属性访问类实例)添加了以下属性和方法:
    • public System.Windows.Forms.TabPage this[bool alwaysAsBase, int index] - 布尔值 'alwaysAsBase' 在代码中未使用;它仅用于区分签名。
    • public TDHTabCtl.TdhTabPage this[string text]
    • public System.Windows.Forms.TabPage this[bool alwaysAsBase, string text]
    • public bool IsTdhTabPage(int index)
    • public new bool Contains(System.Windows.Forms.TabPage theTabPage)
    • public new void Add(System.Windows.Forms.TabPage theTabPage)
    • public new void AddRange(params System.Windows.Forms.TabPage[] theTabPages)
    • public void Insert(int index, System.Windows.Forms.TabPage theTabPage)
    • public void InsertRange(int index, params System.Windows.Forms.TabPage[] theTabPages)
    • public bool RemoveRange(int indexStart, int indexEnd)
  • 2008 年 8 月 9 日:TdhTabCtl ver 1.0.021 - 1.0.021
    解决了 TDHTabCtl.TdhTabCtl 类的设计时 ContextMenu 不识别 TDHTabCtl.TdhTabPage 类的问题(因此,“添加选项卡”MenuItem 添加的是标准的 System.Windows.Forms.TabPage 控件)。这些更改不会修改 TDHTabCtl.TdhTabCtl 类的接口。
  • 2008 年 8 月 10 日:将 TdhTabCtl ver 1.0.021 提交到 The Code Project。
© . All rights reserved.