Tuesday, March 13, 2012

TabItem Header and Content as UserControls in ObservableCollection

Here is a little example project I made to allow the tabitem header to be of a usercontrol and tabitem content to be of another usercontrol. It allows for adding and removing tabitems in the same way internet explorer functions. I have also included a flag button next to the close button on each tab item that you can toggle.

This is a full MVVM application written in VB.Net.




 




Imports System.Collections.ObjectModel
Imports System.ComponentModel

Namespace ViewModel
    Public Class TabControlVM
        Implements INotifyPropertyChanged

#Region "Properties"

        ''Holds collection of tab items
        Private _myTab As ObservableCollection(Of Model.MyTab) = Nothing
        Public Property MyTabCollection As ObservableCollection(Of Model.MyTab)
            Get
                Return _myTab
            End Get
            Set(ByVal value As ObservableCollection(Of Model.MyTab))
                _myTab = value
                OnPropertyChanged("MyTabCollection")
            End Set
        End Property

        ''Selected tab item index
        Private _selectedIndex As Integer = 0
        Public Property SelectedIndex As Integer
            Get
                Return _selectedIndex
            End Get
            Set(ByVal value As Integer)
                _selectedIndex = value
                OnPropertyChanged("SelectedIndex")
            End Set
        End Property

#End Region

#Region "Relay Commands"

        Private _addTab As RelayCommand = Nothing
        Public ReadOnly Property AddTabCommand() As ICommand
            Get
                If _addTab Is Nothing Then
                    Dim commandAction As New Action(Of Object)(AddressOf Me.AddTab)
                    _addTab = New RelayCommand(commandAction)
                End If
                Return _addTab
            End Get
        End Property

        Private _removeTab As RelayCommand = Nothing
        Public ReadOnly Property RemoveTabCommand() As ICommand
            Get
                If _removeTab Is Nothing Then
                    Dim commandAction As New Action(Of Object)(AddressOf Me.RemoveTab)
                    _removeTab = New RelayCommand(commandAction)
                End If
                Return _removeTab
            End Get
        End Property

#End Region

#Region "Constructor"

        ''' <summary>
        ''' <para>Creates a new obeservable collection with tab items for tab control.
        ''' Uses usercontrol for tab item header and content.</para>
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()

            ''Create a MyTab collection (tabitems)
            _myTab = New ObservableCollection(Of Model.MyTab)

            ''Create tabitem header object
            Dim tabHeaderObj = New TabHeaderAddUC
            tabHeaderObj.DataContext = Me

            ''Create the Add Tab content object
            Dim tabContentObj As New TabContentAddUC
            tabContentObj.DataContext = Me

            ''Add to collection
            _myTab.Add(New Model.MyTab(tabHeaderObj, tabContentObj, 0))

        End Sub

#End Region

#Region "Add Tab"

        ''' <summary>
        ''' <para>Method which creates a new tabitem object and adds it to MyTab collection.</para>
        ''' </summary>
        ''' <param name="obj">Object of command parameter from view.</param>
        ''' <remarks></remarks>
        Private Sub AddTab(ByVal obj As Object)

            ''Checks the highest MyTab.Index which will be used when assigingin
            ''a new tab item (index + 1)
            Dim i As Integer = 0
            For Each tb As Model.MyTab In MyTabCollection
                If tb.Index > i Then i = tb.Index
            Next

            ''Create tabitem header object
            Dim tabHeaderObj = New TabHeaderUC
            ''Enable if you want to explicitly set view model class here instead of in the view
            ''-----------------------------------------------------------------------------
            tabHeaderObj.DataContext = New ViewModel.TabHeaderVM With {.HeaderText = "Tab item " & i}
            ''-----------------------------------------------------------------------------

            ''Create tabitem content object
            Dim tabContentObj As New TabContentMyContentUC
            tabContentObj.DataContext = Me

            ''Have the add tab on the left side
            ''-----------------------------------------------------------------------------
            ''Add to collection
            'MyTabCollection.Add(New Model.MyTab(tabHeaderObj, tabContentObj, i + 1))
            ''-----------------------------------------------------------------------------

            ''Have the add tab on the right side
            ''-----------------------------------------------------------------------------
            ''Insert to collection
            MyTabCollection.Insert(i, New Model.MyTab(tabHeaderObj, tabContentObj, i + 1))
            ''-----------------------------------------------------------------------------

            ''Set the highest tab item to visible
            SelectedIndex = i

        End Sub

#End Region

#Region "Remove Tab"

        ''' <summary>
        ''' <para>Removes the tab item at specified index.</para>
        ''' </summary>
        ''' <param name="obj">Object of command parameter from view containing the TabItem.</param>
        ''' <remarks></remarks>
        Private Sub RemoveTab(ByVal obj As Object)

            ''Gets the tabitem MyTab.Index from tabitem Tag and
            ''removes the item at this location from the collection
            Dim tabItemObj = DirectCast(obj, TabItem)

            ''Have the add tab on the left side
            ''-----------------------------------------------------------------------------
            'If Not tabItemObj.Tag = 0 Then MyTabCollection.RemoveAt(tabItemObj.Tag)
            ''-----------------------------------------------------------------------------

            ''Have the add tab on the right side
            ''-----------------------------------------------------------------------------
            If Not tabItemObj.Tag = 0 Then MyTabCollection.RemoveAt(tabItemObj.Tag - 1)           
            ''-----------------------------------------------------------------------------

            ''Rebuild the MyTab.Index to match tabitem index
            Dim j As Integer = 1
            For Each item As Model.MyTab In _myTab
                If Not item.Index = 0 Then
                    item.Index = j
                    j += 1
                End If
            Next

            ''Set the highest tab item to visible
            SelectedIndex = j
        End Sub

#End Region

#Region "INotifyPropertyChanged"

        Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Protected Overridable Sub OnPropertyChanged(ByVal PropertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))

        End Sub

#End Region

    End Class

End Namespace

Imports System.ComponentModel

Namespace Model
    Public Class MyTab
        Implements INotifyPropertyChanged

#Region "Properties"

        ''Tab item header usercontrol
        Private _header As UserControl = Nothing
        Property Header As UserControl
            Get
                Return _header
            End Get
            Set(ByVal value As UserControl)
                _header = value
                OnPropertyChanged("Header")
            End Set
        End Property

        ''Tab content user control
        Private _content As UserControl = Nothing
        Property Content As UserControl
            Get
                Return _content
            End Get
            Set(ByVal value As UserControl)
                _content = value
                OnPropertyChanged("Content")
            End Set
        End Property

        ''Tab item index in tabcontrol
        Private _indexObj As Integer = 0
        Property Index As Integer
            Get
                Return _indexObj
            End Get
            Set(ByVal value As Integer)
                _indexObj = value
                OnPropertyChanged("Index")
            End Set
        End Property

#End Region

#Region "Constructor"

        ''' <summary>
        ''' <para>Constructor accepting two user controls as parameters for header and content.</para>
        ''' </summary>
        ''' <param name="h">UserControl containing header information.</param>
        ''' <param name="c">UserControl containing content information.</param>
        ''' <param name="i">Integer representing an artificial index of the tabitems.</param>
        ''' <remarks></remarks>
        Public Sub New(ByVal h As UserControl, ByVal c As UserControl, ByVal i As Integer)
            Try
                Header = h
                Content = c
                Index = i
            Catch ex As Exception
                Throw New Exception("Error in MyTab constructor.")
            End Try
        End Sub

#End Region

#Region "INotifyPropertyChanged"

        Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Protected Overridable Sub OnPropertyChanged(ByVal PropertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
        End Sub

#End Region

    End Class
End Namespace

Imports System.Collections.ObjectModel
Imports System.ComponentModel

Namespace ViewModel
    Public Class TabHeaderVM
        Implements INotifyPropertyChanged

#Region "Properties"

        ''Set flag image
        Private _imageSource As String = ""
        Public Property ImageSource As String
            Get
                Return _imageSource
            End Get
            Set(ByVal value As String)
                _imageSource = value
                OnPropertyChanged("ImageSource")
            End Set
        End Property

        ''Header text
        Private _headerText As String = ""
        Public Property HeaderText As String
            Get
                Return _headerText
            End Get
            Set(ByVal value As String)
                _headerText = value
                OnPropertyChanged("HeaderText")
            End Set
        End Property

#End Region

#Region "Relay Commands"

        Private _SetTabItemFlagImageCommand As RelayCommand = Nothing
        Public ReadOnly Property SetTabItemFlagImageCommand() As ICommand
            Get
                If _SetTabItemFlagImageCommand Is Nothing Then
                    Dim commandAction As New Action(Of Object)(AddressOf Me.SetTabItemFlagImage)
                    _SetTabItemFlagImageCommand = New RelayCommand(commandAction)
                End If
                Return _SetTabItemFlagImageCommand
            End Get
        End Property

#End Region

#Region "Constructor"

        ''' <summary>
        ''' <para></para>
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()

        End Sub

#End Region

#Region "Set Flag Visibility"

        Private Sub SetTabItemFlagImage()
            If ImageSource = "" Then
                ImageSource = "/Images/16x16/Flag-red.png"
            ElseIf ImageSource = "/Images/16x16/Flag-red.png" Then
                ImageSource = "/Images/16x16/Flag-gray.png"
            ElseIf ImageSource = "/Images/16x16/Flag-gray.png" Then
                ImageSource = ""
            End If

        End Sub

#End Region

#Region "INotifyPropertyChanged"

        Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Protected Overridable Sub OnPropertyChanged(ByVal PropertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))

        End Sub

#End Region

    End Class

End Namespace


No comments:

Post a Comment