Dynamically Alter a Windows Form Via a User Control and the TabControl in VB.NET and C#
Article source code (both VB.NET and C#): dynamictabs.zip
When designing a form, you don't always know what data an individual user will need to see at run-time. Dynamically changing the form using a combination of a User Control and the TabControl is one way to accomplish this task.
In some scenarios, providing a static user interface is sufficient. However, if the number of business objects to display to the user varies, you need an adaptable means of presentation. One way to accomplish this is to programmatically add new tabs and controls to your UI at runtime. In this example, you will add a new instance of a User Control to a TabPage then add the TabPage to the TabControl on a Windows Form.
Getting Started
Create a new blank Visual Studio Solution (File | New | Blank Solution) named "DynamicTabs". To this solution, you will eventually add a Class Library, a Control Library, and a Windows Forms Library. VB.NET is shown here but the steps are similar if using C#. You will find both VB.NET and C# implementations in the article code.
Create a Class Library
Start by adding a new Class Library (File | Add Project | New Project) called "AccountsClassLibrary". Change the default class name, Class1, to be Accounts.
Define a collection of Account objects which in turn contain a collection of CodeListing objects. The Account class is similar in concept to a database table and the CodeListing class to an individual row within a table. The Selection property of the Account class stores what the user selected in the UI.
Public Class Accounts
Inherits CollectionBase
Public Sub Add(ByVal Value As Account)
Me.InnerList.Add(Value)
End Sub
Default Public ReadOnly Property Item(ByVal Index As Integer) As Account
Get
Return CType(MyBase.InnerList.Item(Index), Account)
End Get
End Property
End Class
Public Class Account
Public CodeListings As New CodeListings()
Public AccountName As String
Public Selection As String
End Class
Public Class CodeListings
Inherits CollectionBase
Public Sub Add(ByVal CodeValue As Integer, _
ByVal CodeDisplayValue As String, ByVal CodeDescription As String)
Dim objCodeListing As New CodeListing()
objCodeListing.CodeValue = CodeValue
objCodeListing.CodeDisplayValue = CodeDisplayValue
objCodeListing.CodeDescription = CodeDescription
Me.InnerList.Add(objCodeListing)
End Sub
End Class
Public Class CodeListing
Public CodeValue As Integer
Public CodeDisplayValue As String
Public CodeDescription As String
End Class
Create a Windows Control Library
Add a new Windows Control Library project to your solution and name it "AccountsWindowsControlLibrary". Rename the default UserControl1 file to be "UserControlAccounts" and change the Name property of the control to be "usercontrolAccounts". Add a text box and a list view to the form as follows:
Property | Value |
Name | textboxSelection |
Text | (blank) |
Anchor | Top, Left, Right |
|
Property | Value |
Name | listviewAccountCodes |
Anchor | Top, Bottom, Left, Right |
FullRowSelect | True |
HideSelection | False |
View | Details |
|

Next add a reference to the AccountsClassLibrary project by going to Project | Add Reference. Select the Projects tab on the form that appears. Highlight the AccountsClassLibrary. Click the Select button to add it to selected components, and then click the OK button.

Create an Account property in the user control in such a way that when the property is set the list view control will be populated. Then create a handler for when the user changes the selected item(s).
Private m_Account As AccountsClassLibrary.Account
Public Property Account() As AccountsClassLibrary.Account
Get
Return m_Account
End Get
Set(ByVal Value As AccountsClassLibrary.Account)
m_Account = Value
Me.listviewAccountCodes.Clear()
Dim objCodeColumn As New System.Windows.Forms.ColumnHeader()
objCodeColumn = Me.listviewAccountCodes.Columns.Add("Code", 100, _
HorizontalAlignment.Center)
Dim objDescriptionColumn As New ColumnHeader()
objDescriptionColumn = Me.listviewAccountCodes.Columns.Add( _
"Description", 100, HorizontalAlignment.Left)
objDescriptionColumn.Width = Me.listviewAccountCodes.Width - _
objCodeColumn.Width
'/Add the rows
Dim objCodeListing As AccountsClassLibrary.CodeListing
Dim objListViewItem As ListViewItem
For Each objCodeListing In m_Account.CodeListings
objListViewItem = New ListViewItem()
objListViewItem.Text = objCodeListing.CodeDisplayValue
'Add the item to display in the description column
objListViewItem.SubItems.Add(objCodeListing.CodeDescription)
'Use the tag property to maintain a reference to the CodeListing
' object
objListViewItem.Tag = objCodeListing
Me.listviewAccountCodes.Items.Add(objListViewItem)
Next
End Set
End Property
Private Sub listviewAccountCodes_SelectedIndexChanged( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles _
listviewAccountCodes.SelectedIndexChanged
Dim objStringBuilder As New System.Text.StringBuilder()
Dim i As Integer
Dim objCodeListing As AccountsClassLibrary.CodeListing
For i = 0 To listviewAccountCodes.SelectedIndices.Count - 1
If objStringBuilder.Length > 0 Then
objStringBuilder.Append(", ")
End If
objCodeListing = New AccountsClassLibrary.CodeListing()
objCodeListing = CType(listviewAccountCodes.SelectedItems(i).Tag, _
AccountsClassLibrary.CodeListing)
'Create a text string to store the user selection based on the real
' value, not the display value
objStringBuilder.Append(objCodeListing.CodeValue.ToString)
Next
Me.textboxSelection.Text = objStringBuilder.ToString
End Sub
Now build the solution so the Windows Control Library will be compiled (Build | Build Solution).
Create a Windows Application Project
Add a new Windows Application project to your solution. Name it "DynamicTabsExample". Set this project to be the startup project (Project | Set as StartUp Project). Add references to both the AccountsClassLibrary and the AccountsWindowsControlLibrary.
Rename the default Form1.vb to FormMain.vb, and change the Name property of the form to formMain. Change the Text property of the form to "Dynamic Tabs Example".
Add a tab control to the form as follows:
Property | Value |
Name | tabcontrolAccounts |
Dock | Fill |
|

Create the Form Load
Add code to the forms load event to fill the Accounts and CodeListings collections. For each item in the Accounts collection add a new tab with the user control in it.
Private m_Accounts As New AccountsClassLibrary.Accounts()
Private Sub formMain_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim objAccount As AccountsClassLibrary.Account
If m_Accounts.Count = 0 Then
objAccount = New AccountsClassLibrary.Account()
objAccount.AccountName = "Table1"
objAccount.CodeListings.Add(1, "001", "Descrip 1")
objAccount.CodeListings.Add(2, "002", "Descrip 2")
objAccount.CodeListings.Add(3, "003", "Descrip 3")
m_Accounts.Add(objAccount)
objAccount = New AccountsClassLibrary.Account()
objAccount.AccountName = "Table2"
objAccount.CodeListings.Add(10, "010", "Descrip 10")
objAccount.CodeListings.Add(20, "020", "Descrip 20")
objAccount.CodeListings.Add(30, "030", "Descrip 30")
m_Accounts.Add(objAccount)
objAccount = New AccountsClassLibrary.Account()
objAccount.AccountName = "Table3"
objAccount.CodeListings.Add(100, "100", "Descrip 100")
objAccount.CodeListings.Add(200, "200", "Descrip 200")
objAccount.CodeListings.Add(300, "300", "Descrip 300")
m_Accounts.Add(objAccount)
Me.tabcontrolAccounts.TabPages.Clear()
For Each objAccount In m_Accounts
Dim objTabPage As New TabPage(objAccount.AccountName)
Dim objControl As New _
AccountsWindowsControlLibrary.usercontrolAccounts()
objTabPage.Controls.Add(objControl)
'/Add to control before setting the Account property so internal
' resizing works correctly
Me.tabcontrolAccounts.TabPages.Add(objTabPage)
With objControl
.Dock = DockStyle.Fill
.Account = objAccount
End With
Next
End If
End Sub
The last thing to change is to set the renamed form as the startup object. In the Solution Explorer, select the DynamicTabsExample project. From the main menu select Project | Properties. Change the "Startup object" dropdown to formMain and click OK.

Build the solution and you're ready to run the project.

Select items in the list on each tab. You can see that each one is operating independently.
Summary
Using dynamic tabs can enhance the user experience and allows code re-use via a User Control.
Related devCity.NET articles: