Click here to Skip to main content
15,891,204 members
Articles / Desktop Programming / Windows Forms

MS Office-like TaskPane Control

Rate me:
Please Sign up or sign in to vote.
4.85/5 (25 votes)
24 Apr 2007CPOL10 min read 128.9K   8.4K   262  
A .NET TaskPane control, with full design-time support.
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Windows.Forms.Design
Imports Ascend.Windows.Forms

Public Enum DisplayStyle
    OfficeXP
    Office2003
End Enum

'http://msdn.microsoft.com/msdnmag/issues/05/07/DesignerActions/#S7
'http://www.codeproject.com/useritems/TheCodeProject_DesignTime.asp

<Designer(GetType(TaskPaneDesigner))> _
Public Class TaskPane
    Inherits System.Windows.Forms.UserControl

    Public Event TaskPaneCloseClick(ByVal sender As Object, ByVal e As EventArgs)
    Public Event SelectedIndexChanging(ByVal sender As Object, ByVal e As CancelEventArgs)
    Public Event SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

#Region "   Member Variables"
    Friend Const DefaultCornerRadius As Integer = 8
    Friend Shared NavigationAreaSize As New Size(87, 19)
    Private m_HardMinimumSize As New Size(100, 60)

    Private m_NavigationStyle As DisplayStyle = DisplayStyle.Office2003
    Private m_CaptionStyle As DisplayStyle = DisplayStyle.Office2003

    Private m_TaskPanePages As New TaskPanePageCollection
    Private m_NavigationPanelAppearance As NavigationPanelAppearance
    Private m_HomePane As TaskPanePage = Nothing

    Private m_CurrentIndex As Integer = -1
    Private m_IsClearingCollection As Boolean
#End Region

#Region "   Properties"
    Public Property SelectedIndex() As Integer
        Get
            Return m_CurrentIndex
        End Get
        Set(ByVal value As Integer)
            SelectTaskPanePage(value)
        End Set
    End Property

    Public Property SelectedPage() As TaskPanePage
        Get
            If m_CurrentIndex >= 0 AndAlso m_CurrentIndex < m_TaskPanePages.Count Then
                Return m_TaskPanePages(m_CurrentIndex)
            Else
                Return Nothing
            End If
        End Get
        Set(ByVal value As TaskPanePage)
            SelectTaskPanePage(value)
        End Set
    End Property

    <DefaultValue(GetType(DisplayStyle), "Office2003"), RefreshProperties(RefreshProperties.All)> _
    Public Property NavigationStyle() As DisplayStyle
        Get
            Return m_NavigationStyle
        End Get
        Set(ByVal value As DisplayStyle)
            If m_NavigationStyle = value Then Return

            m_NavigationStyle = value

            UpdateDisplayStyles()
        End Set
    End Property

    <DefaultValue(GetType(DisplayStyle), "Office2003")> _
    Public Property CaptionStyle() As DisplayStyle
        Get
            Return m_CaptionStyle
        End Get
        Set(ByVal value As DisplayStyle)
            m_CaptionStyle = value

            UpdateDisplayStyles()
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    Public ReadOnly Property NavigationPanelAppearance() As NavigationPanelAppearance
        Get
            Return m_NavigationPanelAppearance
        End Get
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    Public ReadOnly Property TaskPanePages() As TaskPanePageCollection
        Get
            Return m_TaskPanePages
        End Get
    End Property

    ''' <summary>
    ''' Indicates whether the panel should have a border
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Description("Indicates whether the panel should have a border"), _
    DefaultValue(GetType(BorderStyle), "None")> _
    Public Shadows Property BorderStyle() As BorderStyle
        Get
            Return MyBase.BorderStyle
        End Get
        Set(ByVal value As BorderStyle)
            ' Apparently, the designer doesn't bother to re-calculate
            ' the control bounds when the border changes until a genuine
            ' resize event occurs.  This override is intended to fix that
            ' problem.
            MyBase.BorderStyle = value

            Me.PerformLayout()
            Me.Invalidate()
        End Set
    End Property

    <DefaultValue(True)> _
    Public Property AllowClose() As Boolean
        Get
            Return btnClose.Enabled
        End Get
        Set(ByVal value As Boolean)
            btnClose.Enabled = value
        End Set
    End Property

    <DefaultValue(True)> _
    Public Property ShowClose() As Boolean
        Get
            Return btnClose.Visible
        End Get
        Set(ByVal value As Boolean)
            btnClose.Visible = value

            UpdateDropdownSize()
        End Set
    End Property

    Private Function ShouldSerializeStartPane() As Boolean
        Return m_HomePane IsNot Nothing
    End Function

    Public Property StartPane() As TaskPanePage
        Get
            Return m_HomePane
        End Get
        Set(ByVal value As TaskPanePage)
            m_HomePane = value

            Me.btnNavHome.Enabled = (btnToolTaskTools.Tag IsNot m_HomePane)
        End Set
    End Property

    <DefaultValue(False)> _
    Public Property ShowImageColumnInDropdown() As Boolean
        Get
            Return CType(btnToolTaskTools.DropDown, ToolStripDropDownMenu).ShowImageMargin
        End Get
        Set(ByVal value As Boolean)
            CType(btnToolTaskTools.DropDown, ToolStripDropDownMenu).ShowImageMargin = value
        End Set
    End Property
#End Region

#Region "   Constructor"
    Public Sub New()
        MyBase.New()

        'This call is required by the Component Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
        SetStyle(ControlStyles.ResizeRedraw, True)
        SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        SetStyle(ControlStyles.SupportsTransparentBackColor, False)
        Me.DoubleBuffered = True

        MyBase.MinimumSize = m_HardMinimumSize
        DirectCast(btnToolTaskTools.DropDown, ToolStripDropDownMenu).ShowCheckMargin = True
        btnToolTaskTools.DropDownDirection = ToolStripDropDownDirection.BelowLeft
        m_NavigationPanelAppearance = New NavigationPanelAppearance(pnlNavButtons)

        AddHandler m_TaskPanePages.CollectionChanged, AddressOf TaskPaneToolCollectionChanged

        UpdateCaptionStyle()
    End Sub
#End Region

#Region "   Overrides"
    Private Function ShouldSerializeMinimumSize() As Boolean
        Return MyBase.MinimumSize <> m_HardMinimumSize
    End Function

    Public Overrides Property MinimumSize() As System.Drawing.Size
        Get
            Return MyBase.MinimumSize
        End Get
        Set(ByVal value As System.Drawing.Size)
            If value = MyBase.MinimumSize Then Return

            If value.Width < m_HardMinimumSize.Width Then
                value.Width = m_HardMinimumSize.Width
            End If
            If value.Height < m_HardMinimumSize.Height Then
                value.Height = m_HardMinimumSize.Height
            End If

            MyBase.MinimumSize = value
        End Set
    End Property

    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
        MyBase.OnResize(e)
        UpdateDropdownSize()
    End Sub

    Protected Overrides Sub OnControlAdded(ByVal e As System.Windows.Forms.ControlEventArgs)
        MyBase.OnControlAdded(e)

        If Not e.Control Is toolStripToolsButtons AndAlso Not e.Control Is pnlNavButtons Then
            e.Control.BringToFront()
        End If
    End Sub

    Protected Overrides Sub OnControlRemoved(ByVal e As System.Windows.Forms.ControlEventArgs)
        MyBase.OnControlRemoved(e)
        If m_IsClearingCollection Then Return

        If TypeOf (e.Control) Is TaskPanePage Then
            Dim page As TaskPanePage = DirectCast(e.Control, TaskPanePage)

            m_TaskPanePages.Remove(page)
        End If
    End Sub

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        MyBase.OnLoad(e)

        GoHome()
    End Sub
#End Region

#Region "   Navigation Handlers"
    Private Sub btnToolTaskTools_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles btnToolTaskTools.MouseDown
        If Me.DesignMode Then
            btnToolTaskTools.ShowDropDown()
        End If
    End Sub

    Private Sub btnToolBack_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles btnToolBack.MouseDown, btnNavBack.MouseDown
        If Me.DesignMode Then
            Back()
        End If
    End Sub

    Private Sub btnToolForward_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles btnToolForward.MouseDown, btnNavForward.MouseDown
        If Me.DesignMode Then
            [Next]()
        End If
    End Sub

    Private Sub btnNavHome_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles btnNavHome.MouseDown
        If Me.DesignMode Then
            GoHome()
        End If
    End Sub

    Private Sub btnClose_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles btnClose.MouseDown
        If Me.DesignMode Then
            MessageBox.Show("Close Clicked")
        End If
    End Sub

    Private Sub Back_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnToolBack.Click, btnNavBack.Click
        Back()
    End Sub

    Private Sub Forward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnToolForward.Click, btnNavForward.Click
        [Next]()
    End Sub

    Private Sub Home_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavHome.Click
        GoHome()
    End Sub

    Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
        RaiseEvent TaskPaneCloseClick(Me, New EventArgs())
    End Sub
#End Region

#Region "   Methods"

#Region "   Style Update Methods"
    Private Sub UpdateDisplayStyles()
        Select Case m_NavigationStyle
            Case DisplayStyle.OfficeXP
                btnToolBack.Visible = True
                btnToolForward.Visible = True
                pnlNavButtons.Visible = False

                NavigationAreaSize = New Size(0, 0)
            Case DisplayStyle.Office2003
                btnToolBack.Visible = False
                btnToolForward.Visible = False
                pnlNavButtons.Visible = True
                pnlNavButtons.BringToFront()

                NavigationAreaSize = pnlNavButtons.Size
            Case Else
                ' Nothing
        End Select

        UpdateCaptionStyle()

        m_NavigationPanelAppearance.Style = m_NavigationStyle

        If Me.DesignMode Then
            Me.Invalidate()
        End If
    End Sub

    Private Sub UpdateCaptionStyle()
        Dim lblTxt As String = "No Tools Yet"
        If m_TaskPanePages.Count > 0 Then
            lblTxt = m_TaskPanePages(m_CurrentIndex).Caption
        End If

        Select Case m_CaptionStyle
            Case DisplayStyle.OfficeXP
                lblToolTaskCaption.Text = lblTxt
                btnToolTaskTools.Text = ""
                btnToolTaskTools.AutoSize = True
            Case DisplayStyle.Office2003
                btnToolTaskTools.Text = lblTxt
                lblToolTaskCaption.Text = ""
                btnToolTaskTools.AutoSize = False
            Case Else
                ' Nothing
        End Select

        UpdateDropdownSize()
    End Sub

    Private Sub UpdateDropdownSize()
        If m_CaptionStyle = DisplayStyle.Office2003 Then
            btnToolTaskTools.AutoSize = False

            ' Calculate all non-dropdown space
            Dim leftBound As Integer = btnToolBack.Width + btnToolBack.Margin.Horizontal _
                                       + btnToolForward.Width + btnToolForward.Margin.Horizontal _
                                       + lblToolTaskCaption.Width + lblToolTaskCaption.Margin.Horizontal _
                                       + btnClose.Width

            If Not btnToolBack.Visible Then
                leftBound = leftBound - btnToolBack.Width - btnToolBack.Margin.Horizontal
            End If
            If Not btnToolForward.Visible Then
                leftBound = leftBound - btnToolForward.Width - btnToolForward.Margin.Horizontal
            End If
            If Not lblToolTaskCaption.Visible Then
                leftBound -= lblToolTaskCaption.Width '- lblToolTaskCaption.Margin.Horizontal
                'ElseIf lblToolTaskCaption.Width = 0 Then
                '    leftBound -= lblToolTaskCaption.Margin.Horizontal
            End If
            If Not btnClose.Visible Then
                leftBound = leftBound - btnClose.Width
            End If

            ' autosized label now
            btnToolTaskTools.Width = toolStripToolsButtons.Width - leftBound - 1

            If btnToolTaskTools.DropDown IsNot Nothing Then
                ' Temporarily allow the drop down to autosize
                ' This forces it to properly calculate its height
                ' from its dropdown items
                btnToolTaskTools.DropDown.AutoSize = True
                ' Retake control of it to make it be full width.
                btnToolTaskTools.DropDown.AutoSize = False
                btnToolTaskTools.DropDown.Width = btnToolTaskTools.Width
            End If
        Else
            btnToolTaskTools.AutoSize = True
            If btnToolTaskTools.DropDown IsNot Nothing Then
                btnToolTaskTools.DropDown.AutoSize = True
            End If
        End If

        For Each item As TaskPanePage In m_TaskPanePages
            item.Style = m_NavigationStyle

            If m_CaptionStyle = DisplayStyle.Office2003 Then
                item.MenuItem.AutoSize = False
                item.MenuItem.Width = btnToolTaskTools.Width - 1
            Else
                item.MenuItem.AutoSize = True
            End If
        Next
    End Sub

    Private Sub UpdateCaption(ByVal caption As String, ByVal img As Image)
        If m_CaptionStyle = DisplayStyle.OfficeXP Then
            btnToolTaskTools.Text = ""
            lblToolTaskCaption.Text = caption
        Else
            btnToolTaskTools.Text = caption
            lblToolTaskCaption.Text = ""
        End If
        lblToolTaskCaption.Image = img
    End Sub

    Private Sub UpdateNavigationButtonStates()
        Me.btnNavForward.Enabled = (m_CurrentIndex < btnToolTaskTools.DropDownItems.Count - 1)
        Me.btnToolForward.Enabled = (m_CurrentIndex < btnToolTaskTools.DropDownItems.Count - 1)

        Me.btnNavBack.Enabled = (m_CurrentIndex <> 0)
        Me.btnToolBack.Enabled = (m_CurrentIndex <> 0)
        If m_HomePane IsNot Nothing Then
            Me.btnNavHome.Enabled = (btnToolTaskTools.Tag IsNot m_HomePane.MenuItem)
        Else
            Me.btnNavHome.Enabled = (m_CurrentIndex <> 0)
        End If
    End Sub
#End Region

    Private Sub TaskPaneToolCollectionChanged(ByVal sender As Object, ByVal e As TaskPanePageCollectionChangedEventArgs)
        If e.ChangeType = CollectionChangeType.Clear Then
            m_IsClearingCollection = True
            For Each tItem As TaskPanePage In m_TaskPanePages
                Me.Controls.Remove(tItem)
            Next
            btnToolTaskTools.DropDownItems.Clear()
            btnToolTaskTools.Enabled = False
            m_IsClearingCollection = False
            Return
        End If

        Select Case e.ChangeType
            Case CollectionChangeType.Insert
                CreateTaskPanePage(e.Index, e.Pane)
            Case CollectionChangeType.Remove
                RemoveTaskPanePage(e.Pane)
            Case CollectionChangeType.Set
                e.Pane.MenuItem.Text = e.Pane.Caption
                e.Pane.MenuItem.Image = e.Pane.CaptionImage
        End Select
    End Sub

    Private Sub RemoveTaskPanePage(ByVal pane As TaskPanePage)
        Me.Controls.Remove(pane)
        RemoveHandler pane.CaptionChanged, AddressOf PageCaptionChanged
        RemoveHandler pane.CaptionImageChanged, AddressOf PageCaptionImageChanged
        RemoveHandler pane.MenuItem.Click, AddressOf TaskMenuItemClicked

        btnToolTaskTools.DropDownItems.Remove(pane.MenuItem)
        If btnToolTaskTools.DropDownItems.Count = 0 Then
            UpdateCaption("No Tools Yet", Nothing)
            btnToolTaskTools.Enabled = False

            pnlNavButtons.Parent = Me
            pnlNavButtons.BringToFront()
            pnlNavButtons.Location = New Point(0, btnToolTaskTools.Height)
        Else
            btnToolTaskTools.Enabled = True
            If m_CurrentIndex = 0 Then
                btnToolTaskTools.DropDownItems(m_CurrentIndex).PerformClick()
            Else
                btnToolTaskTools.DropDownItems(m_CurrentIndex - 1).PerformClick()
            End If
        End If

        UpdateDropdownSize()
        Me.Invalidate()
    End Sub

    Private Sub CreateTaskPanePage(ByVal idx As Integer, ByVal item As TaskPanePage)
        Dim menu As New ToolStripMenuItem
        menu.Text = item.Caption
        menu.Tag = item
        'menu.CheckOnClick = True
        menu.Image = item.CaptionImage
        menu.ImageScaling = ToolStripItemImageScaling.SizeToFit 'None?
        item.MenuItem = menu
        item.Style = m_NavigationStyle

        If item.Caption.Length = 0 AndAlso item.Site IsNot Nothing Then
            item.Caption = item.Site.Name
        End If

        AddHandler item.CaptionChanged, AddressOf PageCaptionChanged
        AddHandler item.CaptionImageChanged, AddressOf PageCaptionImageChanged
        AddHandler menu.Click, AddressOf TaskMenuItemClicked

        Me.Controls.Add(item)
        Me.Controls.SetChildIndex(item, idx)
        btnToolTaskTools.DropDownItems.Add(menu)
        btnToolTaskTools.Enabled = True

        pnlNavButtons.BringToFront()
        UpdateDropdownSize() ' autosizes dropdown if style is VisualStudio2005
        UpdateNavigationButtonStates()

        If m_NavigationStyle = DisplayStyle.Office2003 Then
            menu.AutoSize = False
            menu.Width = btnToolTaskTools.Width - 1
        Else
            menu.AutoSize = True
        End If

        ' Automatically select this page
        menu.PerformClick()
    End Sub

    Private Sub TaskMenuItemClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim menu As ToolStripMenuItem = TryCast(sender, ToolStripMenuItem)
        If menu Is Nothing Then Return

        Dim page As TaskPanePage = TryCast(menu.Tag, TaskPanePage)
        If page Is Nothing Then Return

        SelectTaskPanePage(page)
    End Sub

    Private Sub PageCaptionChanged(ByVal sender As Object, ByVal e As EventArgs)
        Dim pane As TaskPanePage = TryCast(sender, TaskPanePage)
        If pane Is Nothing Then Return

        If btnToolTaskTools.DropDownItems.IndexOf(pane.MenuItem) = m_CurrentIndex Then
            UpdateCaption(pane.Caption, pane.CaptionImage)

            UpdateDropdownSize()

            If Me.DesignMode Then
                ' refresh the control drop area text
                Me.Invalidate()
            End If
        End If
    End Sub

    Private Sub PageCaptionImageChanged(ByVal sender As Object, ByVal e As EventArgs)
        Dim pane As TaskPanePage = TryCast(sender, TaskPanePage)
        If pane Is Nothing Then Return

        If btnToolTaskTools.DropDownItems.IndexOf(pane.MenuItem) = m_CurrentIndex Then
            UpdateCaption(pane.Caption, pane.CaptionImage)

            UpdateDropdownSize()
        End If
    End Sub

    ' Navigation

    Public Sub Back()
        If m_CurrentIndex > 0 AndAlso m_CurrentIndex < btnToolTaskTools.DropDownItems.Count Then
            btnToolTaskTools.DropDownItems(m_CurrentIndex - 1).PerformClick()
        End If
    End Sub

    Public Sub [Next]()
        If m_CurrentIndex < btnToolTaskTools.DropDownItems.Count - 1 Then
            btnToolTaskTools.DropDownItems(m_CurrentIndex + 1).PerformClick()
        End If
    End Sub

    Public Sub GoHome()
        If m_HomePane IsNot Nothing Then
            m_HomePane.MenuItem.PerformClick()
        ElseIf btnToolTaskTools.DropDownItems.Count > 0 Then
            btnToolTaskTools.DropDownItems(0).PerformClick()
        End If
    End Sub

    Public Sub SelectTaskPanePage(ByVal index As Integer)
        If index >= 0 AndAlso index < m_TaskPanePages.Count Then
            SelectTaskPanePage(m_TaskPanePages(index))
        End If
    End Sub

    Public Sub SelectTaskPanePage(ByVal page As TaskPanePage)
        Dim args As New CancelEventArgs
        RaiseEvent SelectedIndexChanging(Me, args)

        If args.Cancel Then
            Return
        End If

        If page Is Nothing Then Return
        If page.MenuItem Is Nothing Then Return

        Dim menu As ToolStripMenuItem = page.MenuItem

        ' Uncheck all items
        For Each mnu As ToolStripMenuItem In btnToolTaskTools.DropDownItems
            mnu.Checked = False
        Next

        ' Now set the current item
        menu.Checked = True
        btnToolTaskTools.Tag = menu

        UpdateCaption(menu.Text, menu.Image)

        m_CurrentIndex = btnToolTaskTools.DropDownItems.IndexOf(menu)
        page.Visible = True
        page.BringToFront()

        pnlNavButtons.Parent = page
        pnlNavButtons.Location = New Point(0, 1)
        If pnlNavButtons.Visible Then
            ' Has the effect of putting controls Docked to the page
            ' at the top of the z-order, forcing them to not overlap
            ' the nav panel.  This is because items at the bottom of the
            ' z-order are guaranteed to be displayed if items above it have
            ' an overlapping dock state.
            pnlNavButtons.SendToBack()
        End If

        UpdateDropdownSize() ' autosizes dropdown if style is VisualStudio2005
        UpdateNavigationButtonStates()

        Me.Focus()
        page.Focus()

        RaiseEvent SelectedIndexChanged(Me, New EventArgs())
    End Sub
#End Region

End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Microsoft
United States United States
I did some stuff.. I live in Seattle.. now I work for Live Search at Microsoft Smile | :)

Comments and Discussions