TdhTabCtl - Firefox 式子类化 TabControl 和 TabPage
本文介绍了具有类似 Firefox 的选项卡按钮和其他增强功能的 .NET 子类化 TabControl 和 TabPage 控件。
简介和功能
我之前提交给 The Code Project 的一篇文章(TdhEditBox)源于本文介绍的项目:TDHTabCtl
,其中包含子类化控件 TdhTabCtl
(继承自 System.Windows.Forms.TabControl
)和 TdhTabPage
(继承自 System.Windows.Forms.TabPage
)。
TDHTabCtl
项目基于 "vijayaprasen" 最初编写的有趣的子类化 TabControl
和 TabPage
控件(参见:“类似 Firefox 的 Tab Control”),通过研究这些代码我学到了很多。当我开始进行这个项目时,我原本只打算将 "vijayaprasen" 的 .NET 2.0 代码转换为 .NET 1.1 代码,然后做一个相对简单但重要的更改(即向项目中添加一个子类化的 TabPageCollection
类)。结果,我最终完全重写了原始项目,并添加了使我的版本与原始版本不同的功能。
TDHControls.TDHTabCtl.TdhTabCtl
和 TDHControls.TDHTabCtl.TdhTabPage
控件的一些功能是:
TdhTabCtl
- 允许或禁止关闭任何
TdhTabPage
。此操作会触发一个事件。 - 在关闭任何
TdhTabPage
之前需要或不需要确认。 - 允许或禁止打开新的
TdhTabPage
。此操作会触发一个事件。 - 允许或禁止“重命名”(更改选项卡上的文本)任何
TdhTabPage
。此操作会触发一个事件。 - 允许或禁止重新排序
TdhTabPage
。此操作会触发一个事件。 - 允许或禁止在任何
TdhTabPage
的选项卡/标题上进行右键单击上下文菜单。
TdhTabPage
- 允许或禁止关闭单个
TdhTabPage
。此操作会触发一个事件。 - 在关闭单个
TdhTabPage
之前需要或不需要确认。 - 允许或禁止在单个
TdhTabPage
上进行右键单击上下文菜单。 - 在单个
TdhTabPage
上显示或不显示“关闭”按钮。 - 在单个
TdhTabPage
上显示或不显示“菜单”按钮。 - 默认菜单不必单独分配给每个
TdhTabPage
。 - (可选)分配给单个
TdhTabPage
的上下文菜单将在显示该TdhTabPage
的菜单时合并到默认菜单中。
事件
TdhTabCtl
和 TdhTabPage
控件会触发事件,以通知客户端代码何时执行了这些“增强”功能(添加、删除、重命名、重新排序)(例如,当用户关闭 TdhTabPage
时)。
自定义上下文菜单
任何 TdhTabPage
控件都可以选择性地分配一个唯一的上下文菜单。在演示截图的“Page3”上可以看到分配了一个这样的唯一/单独的上下文菜单。
在应用程序中使用 TdhTabCtl 和 TdhTabPage 控件
TDHTabCtl 项目是使用 VS2002 (.NET 1.0) 编写(和编译)的,目的是让其他开发者无论使用什么 .NET 版本都能方便地访问源代码。
要按原样使用 TdhTabCtl
和 TdhTabPage
类,请在您的项目中添加对类库 '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 的最终用户体验
由于 TdhTabCtl
和 TdhTabPage
控件继承自标准的 System.Windows.Forms.TabControl
和 System.Windows.Forms.TabPage
控件,因此可以预期最终用户会轻松理解它们。新增的功能应该是自 explanatory 的。
“重命名” TdhTabPage 控件
启用此功能后,可以通过 TdhTabCtl
控件的内置上下文菜单访问,允许最终用户更改 TdhTabPage
控件上显示的 .Text
值。
“重命名” TdhTabPage
控件的功能的目的是使用户与之的交互直观且轻松。因此,无论是接受还是拒绝重命名操作,都不需要特殊的用户操作或辅助控件来关闭编辑框。相反,编辑框可以通过键盘或鼠标行为关闭。
具体来说
- 单击编辑框外部的任何位置都会关闭它并拒绝该操作。
- 使用 [Escape] 键关闭编辑框并拒绝该操作。
- 使用 [Return] 键关闭编辑框并接受该操作。
- 使用 [Tab] 键关闭编辑框并接受该操作。
重新排序 TdhTabCtl 的 TdhTabPages
启用此功能后,可以通过 TdhTabCtl
控件的内置上下文菜单访问。以下截图显示了此功能的用法。
重新排序 TdhTabCtl
的 TdhTabPage
集合并不像简单的拖放操作那样直接。另一方面,此操作的代码允许最终用户预览最终结果并接受或拒绝该操作。
TdhTabCtl 和 TdhTabPage 编程接口
TdhTabCtl
控件接口的新颖属性和方法(除了继承的成员)是:
public TDHTabCtl.TdhTabPageCollection TdhTabPages
- 此属性扩展了标准的 .TabPages
属性;它了解System.Windows.Forms.TabPage
和TDHTabCtl.TdhTabPage
类,并提供附加方法和属性(例如,'Insert()' 方法)。public new System.Windows.Forms.TabControl.TabPageCollection TabPages
- 这是System.Windows.Forms.TabControl
的标准 .TabPages
属性;它只了解System.Windows.Forms.TabPage
类。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
对象上绘制/显示“菜单按钮”。
以下属性用于任何不是 TDHTabCtl.TdhTabPage
(或其继承类)的 Tab 对象。它们对应于 TDHTabCtl.TdhTabPage
类的某些新颖属性。由于标准的 System.Windows.Forms.TabPage
控件确实具有这些属性,因此 TDHTabCtl.TdhTabCtl
类在设置值时会使用对于任何非 TDHTabCtl.TdhTabPage
的 Tab 对象。
- 与以下所有方法一起
- 如果
OnTabEvents
EventHandler 已附加到TDHTabCtl.TdhTabCtl
控件,则事件将触发,如同用户手动删除/关闭 Tab 对象一样。 - 参数 '
overridePermission
' 的“true”值将覆盖用户尝试手动删除/关闭 Tab 对象时通常适用于给定对象的 .TabsAllowClose
、.TabsBase_AllowClose
或 .TabAllowClose
。 - 返回值指示是否在 .
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.TdhTabPage
和System.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。