Introduction and Features
A previous article I'd submitted to The Code Project (TdhEditBox) grew out of the project described in this article: TDHTabCtl
, which contains the subclassed controls TdhTabCtl
(inheriting from System.Windows.Forms.TabControl
) and TdhTabPage
(inheriting from System.Windows.Forms.TabPage
).
The TDHTabCtl
project is based on the interesting subclassed TabControl
and TabPage
controls originally written by "vijayaprasen" (see: "Firefox-like Tab Control"), and from which I learned quite a bit in studying the code. When I started working on this project, I had intended only to convert "vijayaprasen"'s .NET 2.0 code into .NET 1.1 code, and then to make a relatively simple, though important, change (i.e., adding a subclassed TabPageCollection
class to the project). In the event, I ended up completely rewriting the original project and adding functionality which differentiates my version from the original.
Some features of the TDHControls.TDHTabCtl.TdhTabCtl
and TDHControls.TDHTabCtl.TdhTabPage
controls are:
TdhTabCtl
- Allows or disallows any
TdhTabPage
to be closed. This action raises an event. - Requires or not confirmation before closing any
TdhTabPage
. - Allows or disallows new
TdhTabPage
s to be opened. This action raises an event. - Allows or disallows any
TdhTabPage
to be "renamed" (change text on tab). This action raises an event. - Allows or disallows
TdhTabPage
s to be reordered. This action raises an event. - Allows or disallows right-click ContextMenus on any
TdhTabPage
tabs/headers.
TdhTabPage
- Allows or disallows individual
TdhTabPage
to be closed. This action raises an event. - Requires or not confirmation before closing individual
TdhTabPage
s. - Allows or disallows right-click ContextMenus on individual
TdhTabPage
s. - Shows or not the Close button on individual
TdhTabPage
s. - Shows or not the Menu button on individual
TdhTabPage
s. - Default menu does not have to be individually assigned to each
TdhTabPage
. - ContextMenu (optionally) assigned to individual
TdhTabPage
s is merged into the default menu when the menu is displayed for that TdhTabPage
.
Events
The TdhTabCtl
and TdhTabPage
controls raise events to notify the client code when these "enhanced" functions (Add, Remove, Rename, Reorder) are performed (for instance, when the user closes a TdhTabPage
).
Custom context menu
Any TdhTabPage
control may optionally be assigned a unique ContextMenu. In the demo screenshot, we see that "Page3" has been assigned such a unique/individual ContextMenu.
Using the TdhTabCtl and TdhTabPage Controls with your Application
The TDHTabCtl project was written (and compiled) using VS2002 (.NET 1.0) with the intention that the source code be readily available to other developers regardless of the .NET version they are using.
To use the TdhTabCtl
and TdhTabPage
classes as is, add a reference in your project to the class library 'TDHTabCtl.dll'.
using TDHControls.TDHTabCtl;
private void InitializeComponent()
{
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;
this.tdhTabCtl1.OnTabEvents
+= new TDHControls.TDHTabCtl.TabEventsDelegate(this.tdhTabCtl1_OnTabEvents);
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:
break;
case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabAddRejected:
this.tabControl1.TabPages.Add(e.TabPage);
break;
case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabRemoved:
this.tabControl1.TabPages.Add(e.TabPage);
break;
case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabRenamed:
break;
case TDHControls.TDHTabCtl.TabEventArgs.TabEvents.TabsReordered:
Console.WriteLine("TdhTabPage reordered."
+" OldInd="+ e.TabIndexOld.ToString()
+" NewInd="+ e.TabIndexNew.ToString());
break;
default:
break;
}
}
private void tdhTabCtl1_OnTabsReordered(object sender,
TDHControls.TDHTabCtl.TabsReorderedEventArgs e)
{
for (int idx = 0; idx < e.TabOrder_int.Length; idx++)
{
Console.WriteLine("TdhTabPage reordered."
+" OldInd="+ e.TabOrder_int[idx].ToString()
+" NewInd="+ idx.ToString() );
}
}
The TdhTabCtl and TdhTabPage End-User Experience
As the TdhTabCtl
and TdhTabPage
controls inherit from the standard System.Windows.Forms.TabControl
and System.Windows.Forms.TabPage
controls, the typical end-user can be expected to understand them without difficulty. The added functionality ought to be self-explanatory.
"Renaming" a TdhTabPage Control
When enabled, this function, accessed via the built-in context menu of the TdhTabCtl
control, allows the end-user to change the .Text
value displayed on the tab of the TdhTabPage
control(s).
The intention of the functionality for "renaming" the TdhTabPage
controls is that user interaction with it be intuitive and easy. Thus, no special user actions or secondary controls are necessary to dismiss the edit box, whether to accept or reject the rename-action. Rather, the edit box is dismissed by keyboard or mouse behavior.
Specifically:
- Clicking anywhere outside the edit box dismisses it and rejects the action.
- Use of the [Escape] key dismisses the edit box and rejects the action.
- Use of the [Return] key dismisses the edit box and accepts the action.
- Use of the [Tab] key dismisses the edit box and accepts the action.
Reordering the TdhTabPages of a TdhTabCtl
When enabled, this function is accessed via the built-in context menu of the TdhTabCtl
control. The following screenshot shows this function in use:
Reordering the TdhTabPage
s collection of a TdhTabCtl
isn't as straightforward as a simple drag-and-drop operation. On the other hand, the code for this action allows the end-user to preview the end-result and accept or reject the action.
The TdhTabCtl and TdhTabPage Programming Interface
Novel properties and methods (in addition to the inherited members) of the TdhTabCtl
control's interface are:
public TDHTabCtl.TdhTabPageCollection TdhTabPages
- This property extends the standard .TabPages
property; it knows about both the System.Windows.Forms.TabPage
and TDHTabCtl.TdhTabPage
classes, and provides added methods and properties (for instance, an 'Insert()' method). public new System.Windows.Forms.TabControl.TabPageCollection TabPages
- This is the standard .TabPages
property for a System.Windows.Forms.TabControl
; it knows only about the System.Windows.Forms.TabPage
class. The following properties are used for any Tab object which is not a TDHTabCtl.TdhTabPage
(or inherited). They correspond to certain of the novel properties of the TDHTabCtl.TdhTabPage
class. As the standard System.Windows.Forms.TabPage
control does have these properties, the TDHTabCtl.TdhTabCtl
class uses the set value for any non-TDHTabCtl.TdhTabPage
Tab object.
public bool TabsBase_AllowClose
- This property indicates whether the Tab object may be removed/closed. public bool TabsBase_ConfirmOnClose
- This property indicates whether confirmation is required before removing/closing the Tab object. public bool TabsBase_ContextMenuAllowOpen
- This property indicates whether the "Open New Tab (Basic TabPage)" MenuItem
of the TDHTabCtl.TdhTabCtl
control's built-in context menu is enabled/available.
public bool TabsAllowClose
- This property indicates whether any Tab object of the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
may be removed/closed.public bool TabsAllowContextMenu
- This property indicates whether the TDHTabCtl.TdhTabCtl
control's built-in context menu is to be enabled/available for any Tab object of the control's .TabPageCollection
.public bool TabsConfirmOnClose
- This property indicates whether confirmation is generally required before removing/closing any Tab object of the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public bool TabsContextMenuAllowClose
- This property indicates whether the "Close Tab" MenuItem
s of the TDHTabCtl.TdhTabCtl
control's built-in context menu are to be enabled/available.public bool TabsContextMenuAllowOpen
- This property indicates whether the "Open New Tab" MenuItem
of the TDHTabCtl.TdhTabCtl
control's built-in context menu is to be enabled/available.public bool TabsContextMenuAllowRename
- This property indicates whether the "Rename Tab" MenuItem
of the TDHTabCtl.TdhTabCtl
control's built-in context menu is to be enabled/available.public bool TabsContextMenuAllowReorder
- This property indicates whether the "Reorder Tab Pages" MenuItem
of the TDHTabCtl.TdhTabCtl
control's built-in context menu is to be enabled/available.public bool TabsShowCloseButton
- This This property indicates whether the "close button" may be drawn/shown on any TDHTabCtl.TdhTabPage
object of the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public bool TabsShowMenuButton
- This property indicates whether the "menu button" may be drawn/shown on any TDHTabCtl.TdhTabPage
object of the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.
With all the following methods:
- If an
OnTabEvents
EventHandler is attached to the TDHTabCtl.TdhTabCtl
control, the event will fire as though the user had manually removed/closed the Tab object. - A 'true' value of the '
overridePermission
' argument over-rides any .TabsAllowClose
or .TabsBase_AllowClose
or .TabAllowClose
which would normally apply for the given object were the user attempt to manually remove/close the Tab object. - The return value indicates whether the given object was found in the .
TabPageCollection
and removed, or whether the given index values were valid and the objects with those values removed.
public bool TabPages_Remove(bool overridePermission, TDHTabCtl.TdhTabPage theTabPage)
- This method removes from the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
the given TDHTabCtl.TdhTabPage
object.public bool TabPages_Remove(bool overridePermission, System.Windows.Forms.TabPage theTabPage)
- This method removes from the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
the given (boxed) System.Windows.Forms.TabPage
object.public bool TabPages_RemoveAt(bool overridePermission, int index)
- This method removes from the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
the Tab object with the given index value.public bool TabPages_RemoveRange(bool overridePermission, int indexStart, int indexEnd)
- This method removes from the TDHTabCtl.TdhTabCtl
control's .TabPageCollection
the Tab objects with the given range of index values.
Novel properties (in addition to the inherited members) of the TdhTabPage
control's interface are:
public bool TabAllowClose
- This property indicates whether the specific TDHTabCtl.TdhTabPage
control may be removed/closed.public bool TabAllowContextMenu
- This property indicates whether the TDHTabCtl.TdhTabCtl
control's built-in context menu is to be enabled/available for the the specific TdhTabPage
control.public bool TabConfirmOnClose
- This property indicates whether confirmation is required before removing/closing the specific TDHTabCtl.TdhTabPage
control.public bool TabShowCloseButton
- This property indicates whether the "close button" is to be drawn/shown on the specific TDHTabCtl.TdhTabPage
control.public bool TabShowMenuButton
- This property indicates whether the "menu button" is to be drawn/shown on the specific TDHTabCtl.TdhTabPage
control.
Novel properties and methods (in addition to the inherited members) of the
TDHTabCtl.TdhTabCtl.TdhTabPages
property's interface are:
public new TDHTabCtl.TdhTabPage this[int index]
- This is the main Indexer for the TDHTabCtl.TdhTabCtl.TdhTabPages
property. If the Tab object at the given index in the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
is not an instance of TDHTabCtl.TdhTabPage
(or inherited from it), the returned object is a null object -- thus, if one's project allows both TdhTabCtl.TdhTabPage
s and System.Windows.Forms.TabPage
s to be added to the TdhTabCtl.TdhTabCtl
, it would be better to use next Indexer. Example use: [ this.tdhTabCtl1.TabPages[0].Text = "A TdhTabPage";
] public System.Windows.Forms.TabPage this[bool alwaysAsBase, int index]
- This Indexer returns the Tab object at the given index in the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
(boxed) as a standard System.Windows.Forms.TabPage
. The bool value 'alwaysAsBase
' is not used in the code; it's there only to differentiate the signature. Example use: [ this.tdhTabCtl1.TabPages[true, 0].Text = "A TdhTabPage";
] public TDHTabCtl.TdhTabPage this[string text]
- This Indexer tries to return the TDHTabCtl.TdhTabPage
object in the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
having a .Text
value matching 'text
;' else it returns a null object.public System.Windows.Forms.TabPage this[bool alwaysAsBase, string text]
- This Indexer tries to return the (boxed) System.Windows.Forms.TabPage
object in the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
having a .Text
value matching 'text
;' else it returns a null object.
public bool IsTdhTabPage(int index)
- This method indicates whether the indexed Tab object in the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
is a TDHTabCtl.TdhTabPage
(or inherited from it).public new void Clear()
- This method straightforwardly clears the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public bool Contains(TDHTabCtl.TdhTabPage theTabPage)
- This method indicates whether the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
contains the given TDHTabCtl.TdhTabPage
object.public new bool Contains(System.Windows.Forms.TabPage theTabPage)
- This method indicates whether the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
contains the given System.Windows.Forms.TabPage
object.public void Add(TDHTabCtl.TdhTabPage theTabPage)
- This method adds the given TDHTabCtl.TdhTabPage
object to the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public void Add(System.Windows.Forms.TabPage theTabPage)
- This method adds the given System.Windows.Forms.TabPage
object to the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public void AddRange(params TDHTabCtl.TdhTabPage[] theTabPages)
- This method adds the given TDHTabCtl.TdhTabPage
object array to the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public void AddRange(params System.Windows.Forms.TabPage[] theTabPages)
- This method adds the given System.Windows.Forms.TabPage
object array to the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public void Insert(int index, TDHTabCtl.TdhTabPage theTabPage)
- This method inserts the given TDHTabCtl.TdhTabPage
object into the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
. public void Insert(int index, System.Windows.Forms.TabPage theTabPage)
- This method inserts the given System.Windows.Forms.TabPage
object into the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public void InsertRange(int index, params TDHTabCtl.TdhTabPage[] theTabPages)
- This method inserts the given TDHTabCtl.TdhTabPage
object array into the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public void InsertRange(int index, params System.Windows.Forms.TabPage[] theTabPages)
- This method inserts the given System.Windows.Forms.TabPage
object array into the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
.public bool RemoveRange(int indexStart, int indexEnd)
- This method removes the Tab objects of the parent TDHTabCtl.TdhTabCtl
control's .TabPageCollection
with index values between 'indexStart
' and 'indexEnd
' inclusive.
Event handlers and delegates of the TdhTabCtl
control:
public event TDHControls.TDHTabCtl.TabEventsDelegate OnTabEvents
- The OnTabEvents
event fires when any of these various actions are performed on the TdhTabCtl
control's TdhTabPage
collection:
- Adding a
TdhTabPage
- Initiating but then rejecting the addition of a
TdhTabPage
- Removing/closing a
TdhTabPage
- "Renaming" a
TdhTabPage
- Reordering the
TdhTabPage
collection (Note: If a OnTabsReordered
event handler has been assigned for the TdhTabCtl
control, the OnTabEvents
event does not fire for this action; otherwise, it fires individually for each TdhTabPage
affected.)
The various properties of the TabEventArgs
argument supply specific information about the event, including a reference to the affected TdhTabPage
instance.
public delegate void TabEventsDelegate(object sender, TDHControls.TDHTabCtl.TabEventArgs e)
- This event delegate defines the signature of the OnTabEvents
event handler.public event TDHControls.TDHTabCtl.TabsReorderedEventDelegate OnTabsReordered
- The OnTabsReordered
event fires when the TdhTabCtl
control's TdhTabPage
collection is reordered. Among the properties of the TabsReorderedEventArgs
argument is an ArrayList
of integer values (also accessible as an integer array) which may be used to determine the new tab order as compared to the old.public delegate void TabsReorderedEventDelegate(object sender, TDHControls.TDHTabCtl.TabsReorderedEventArgs e)
- This event delegate defines the signature of the OnTabsReordered
event handler.
History
- 2008 July 29: Submission of
TdhTabCtl
ver 1.0.004 to The Code Project. - 2008 August 1:
TdhTabCtl
ver 1.0.005:
- Fixed an apparent timing issue. When the last (rightmost)
TdhTabPage
was removed/closed via the ContextMenu
, its TabRect was drawn being drawn again after the removal.
- 2008 August 8:
TdhTabCtl
ver 1.0.010 - 1.0.012:
Enabled the TdhTabCtl
control to contain and handle both TdhTabPage
and .Forms.TabPage
controls.
- Renamed the overridden
TdhTabCtl.TabPages
property (i.e. the accessor for the TDHControls.TDHTabCtl.TdhTabPageCollection
class instance) to TdhTabCtl.TdhTabPages
. - Thus the
.TabControl.TabPages
property for the TdhTabCtl
instance returns base.TabPages
.
Added following properties and methods to class 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)
Added following properties and methods to class TDHControls.TDHTabCtl.TdhTabPageCollection
(class instance is accessed via the TdhTabCtl.TdhTabPages
property):
public System.Windows.Forms.TabPage this[bool alwaysAsBase, int index]
- The bool value 'alwaysAsBase
' is not used in the code; it's there only to differentiate the signature. 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 August 9:
TdhTabCtl
ver 1.0.021 - 1.0.021:
Resolved the issue that the design-time ContextMenu
for the TDHTabCtl.TdhTabCtl
class was unaware of the TDHTabCtl.TdhTabPage
class (thus, the "Add Tab" MenuItem added standard System.Windows.Forms.TabPage
controls). These changes don't modify the interface of the TDHTabCtl.TdhTabCtl
class. - 2008 August 10: Submission of
TdhTabCtl
ver 1.0.021 to The Code Project.