Hiding and Showing TabPages in VB .NET - TabManager

4ntibala picture 4ntibala · Aug 5, 2013 · Viewed 10.7k times · Source

I try to hide and show tabpages dynamically at runtime. For this I converted the code of Emile from here to vb.net and the resulted problem is that tabpages cannot be shown anymore after they were hidden. At least not if they were set to invisible somewhere else than where I am trying set them visible.

edit:

After a long discussion i finally came up with a working result. i corrected the calling procedures and now this code works. It can hide and show tabpages that reside on any form, just like the original version does. Thanks go to user varocarbas..

Hiding a tabpage:

clsTabManager.SetInvisible(tabPage)

Showing a tabpage (call from any class/form):

clsTabManager.SetVisible(FormWithTabControl.tabPage, FormWithTabControl.TabControl)

Showing a tabpage (call from Form where TabControl resides):

clsTabManager.SetVisible(tabPage, TabControl)

clsTabmanager:

Public Class clsTabManager

    Private Structure TabPageData
        Friend Index As Integer
        Friend Parent As TabControl
        Friend Page As TabPage

        Friend Sub New(index__1 As Integer, parent__2 As TabControl, page__3 As TabPage)
            Index = index__1
            Parent = parent__2
            Page = page__3
        End Sub

        Friend Shared Function GetKey(tabCtrl As TabControl, tabPage As TabPage) As String
            Dim key As String = ""
            If tabCtrl IsNot Nothing AndAlso tabPage IsNot Nothing Then
                key = [String].Format("{0}:{1}", tabCtrl.Name, tabPage.Name)
            End If
            Return key
        End Function
    End Structure

    Private hiddenPages As New Dictionary(Of String, TabPageData)()

  
    Public Sub SetVisible(page As TabPage, parent As TabControl)
        If parent IsNot Nothing AndAlso Not parent.IsDisposed Then
            Dim tpinfo As TabPageData
            Dim key As String = TabPageData.GetKey(parent, page)

            If hiddenPages.ContainsKey(key) Then
                tpinfo = hiddenPages(key)

                If tpinfo.Index < parent.TabPages.Count Then
                    parent.TabPages.Insert(tpinfo.Index, tpinfo.Page)
                Else
                    ' add the page in the same position it had
                    parent.TabPages.Add(tpinfo.Page)
                End If

                hiddenPages.Remove(key)
            End If
        End If
    End Sub

    Public Sub SetInvisible(page As TabPage)
        If IsVisible(page) Then
            Dim tabCtrl As TabControl = DirectCast(page.Parent, TabControl)
            Dim tpinfo As TabPageData
            tpinfo = New TabPageData(tabCtrl.TabPages.IndexOf(page), tabCtrl, page)
            tabCtrl.TabPages.Remove(page)
            hiddenPages.Add(TabPageData.GetKey(tabCtrl, page), tpinfo)
        End If
    End Sub

    Public Function IsVisible(page As TabPage) As Boolean
        Return page IsNot Nothing AndAlso page.Parent IsNot Nothing
        ' when Parent is null the tab page does not belong to any container
    End Function

    Public Sub CleanUpHiddenPage(page As TabPage)
        For Each info As TabPageData In hiddenPages.Values
            If info.Parent IsNot Nothing AndAlso info.Parent.Equals(DirectCast(page.Parent, TabControl)) Then
                info.Page.Dispose()
            End If
        Next
    End Sub

    Public Sub CleanUpAllHiddenPages()
        For Each info As TabPageData In hiddenPages.Values
            info.Page.Dispose()
        Next
    End Sub

End Class

Answer

varocarbas picture varocarbas · Aug 5, 2013

The conversion you have done of the original C# code is not perfect (you should understand what each part does, instead of copying bit by bit). In the SetVisible/SetInvisible part this is the problem:

Public Shared Function SetInvisible(page As TabPage, frm As Form) 'As Boolean
    page = frm.Controls(page.Name)

    If IsVisible(page) Then
        Dim tabCtrl As TabControl = DirectCast(page.Parent, TabControl)
        Dim tpinfo As TabPageData

        tpinfo = New TabPageData(tabCtrl.TabPages.IndexOf(page), tabCtrl, page)

        tabCtrl.TabPages.Remove(page)
        hiddenPages.Add(TabPageData.GetKey(tabCtrl, page), tpinfo)
    End If
End Function

(this should be a Sub rather than a Function) You are adding a bit not present in the original code: page = frm.Controls(page.Name); I guess that it is an adaptation to make the code work under your specific conditions (you have added to the form a TabPage alone, instead one inside a TabControl, what is the normal behaviour). This would be fine, but you are not adapting the SetVisible function to this reality:

Public Shared Sub SetVisible(page As TabPage, parent As TabControl)
    If parent IsNot Nothing AndAlso Not parent.IsDisposed Then
        Dim tpinfo As TabPageData
        Dim key As String = TabPageData.GetKey(parent, page)

        If hiddenPages.ContainsKey(key) Then
            tpinfo = hiddenPages(key)

            If tpinfo.Index < parent.TabPages.Count Then
                parent.TabPages.Insert(tpinfo.Index, tpinfo.Page)
            Else
                ' add the page in the same position it had
                parent.TabPages.Add(tpinfo.Page)
            End If

            hiddenPages.Remove(key)
        Else
            PrintAllKeys()
        End If
    End If
End Sub

Understand what both functions do: the first one (modified by you) expects a TabPage added to the form directly (thus with no parent TabControl); the second one (as in the original C# code) expects a TabPage with a parent TabControl but your input does not have that. How I know that? If your TabPage would have a TabControl as a parent, the page = frm.Controls(page.Name) would be Nothing.

If you want to use this code you have to provide the expected inputs, that is, TabPages inside a TabControl. Otherwise, you should modify it accordingly (not just one part, all the parts). Easy test for you to understand what is required:

1- Open a new project and add a new TabControl via "Design View".
2- Copy your class but let SetInvisible as in the original version (delete page = frm.Controls(page.Name)).
3- Test your class with the main form and see that it works fine. Sample code (these are the default names when you add a new TabControl):

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load   
    Dim test As clsTabManager = New clsTabManager()

    test.SetInvisible(TabPage1, Me)

    test.SetVisible(TabPage1, TabControl1)
End Sub