Home All Groups Group Topic Archive Search About

Why don't child control events trigger OnBubbleEvent?

Author
10 May 2005 1:56 PM
lisa
I have a control which contains a collection of another control.  The
members of the collection can contain child controls themselves, but
these aren't coded.  They are merely added between the start and end
tags of the child controls in the HTML view.

If I put a button (for instance) inside one of the child controls, the
button's click event causes a postback, but then seems to disappear
entirely.  I tried looking at OnBubbleEvent in the child control and in
the parent control.  Those events are never called.  Nor does the
button's click event in the page containing the control ever get
called.

Is there a way to capture events of such child controls?  I get the
feeling that these child controls aren't being added to the control
hierarchy of the parent control, but they are visible when I do
ParentControl.ChildrenControls(idx).Controls(idx2), so maybe they are
in the control hierarchy.

I'm completely stumped here, and I'd appreciate any suggestions.

TIA,
Lisa

Author
10 May 2005 2:30 PM
Brock Allen
Post your code, as that will help in determining what's going on.

-Brock
DevelopMentor
http://staff.develop.com/ballen



Show quoteHide quote
> I have a control which contains a collection of another control.  The
> members of the collection can contain child controls themselves, but
> these aren't coded.  They are merely added between the start and end
> tags of the child controls in the HTML view.
>
> If I put a button (for instance) inside one of the child controls, the
> button's click event causes a postback, but then seems to disappear
> entirely.  I tried looking at OnBubbleEvent in the child control and
> in the parent control.  Those events are never called.  Nor does the
> button's click event in the page containing the control ever get
> called.
>
> Is there a way to capture events of such child controls?  I get the
> feeling that these child controls aren't being added to the control
> hierarchy of the parent control, but they are visible when I do
> ParentControl.ChildrenControls(idx).Controls(idx2), so maybe they are
> in the control hierarchy.
>
> I'm completely stumped here, and I'd appreciate any suggestions.
>
> TIA,
> Lisa
Author
10 May 2005 5:22 PM
lisa
It's a bit long, but here it is.  I appreciate the help.

Thanks,
Lisa

Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Web.UI
Imports System.IO
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls
'todo: I need to find a way to get the contained controls to postback

<ToolboxData("<{0}:TabControl runat=server></{0}:TabControl>"),
Designer(GetType(TabControlDesigner)), PersistChildren(False),
ParseChildren(True, "Tabs"), DefaultProperty("Tabs"),
DefaultEvent("SelectedIndexChanged")> _
Public Class TabControl
    Inherits WebControl
    Implements INamingContainer, IPostBackDataHandler,
IPostBackEventHandler

    Private _tabs As New TabCollection
    Private _tabsPerRow As Integer = 3
    Private _tabHeight As New Unit(25, UnitType.Pixel)
    Private _width As New Unit(300, UnitType.Pixel)
    Private _height As New Unit(250, UnitType.Pixel)
    Private _selectedIndex As Integer = 0
    Private _autoPostBack As Boolean = False
    Private _tabFontFamily As String = "System"
    Private _tabFontSize As FontUnit = FontUnit.Point(8)
    Private _tabCount As Integer

    Private _tabRows As Integer
    Private _fullRows As Integer
    Private _tabsInPartRow As Integer
    Private _tabsInFullRow As Integer
    Private _fullRowTabWidth As Unit
    Private _partRowTabWidth As Unit
    Private _fullRowRemainder As Integer
    Private _partRowRemainder As Integer
    Private _tabPageHeight As Unit
    Private _rowArray As Pair()()
    Private _selectedRow As Integer
    Private _tabIdx As Integer

    Private _propertyChangedList As DropDownList

#Region "Properties"

    <Category("Behavior"), Description("The collection of tabs."),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)> _
    Public ReadOnly Property Tabs() As TabCollection
        Get
            Return _tabs
        End Get
    End Property

    <Category("Appearance"), Description("The maximum number of tabs in
a row."),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.Attribute)> _
    Public Property TabsPerRow() As Integer
        Get
            Return _tabsPerRow
        End Get
        Set(ByVal Value As Integer)
            If Value < 1 Then
                Value = 1
            End If
            _tabsPerRow = Value
        End Set
    End Property 'TabsPerRow

    <Category("Appearance"), Description("The height of each tab
row.")> _
    Public Property TabHeight() As Unit
        Get
            Return _tabHeight
        End Get
        Set(ByVal Value As Unit)
            If Not Value.Type = UnitType.Pixel Then
                Throw New ArgumentException("TabHeight must be given in
pixels.")
            End If
            _tabHeight = Value
        End Set
    End Property 'TabHeight

    <Category("Appearance"), Description("The width of the control.")>
_
    Public Overrides Property Width() As Unit
        Get
            Return _width
        End Get
        Set(ByVal Value As Unit)
            If Not Value.Type = UnitType.Pixel Then
                Throw New ArgumentException("Width must be given in
pixels.")
            End If
            _width = Value
        End Set
    End Property 'Width

    <Category("Appearance"), Description("The height of the control.")>
_
    Public Overrides Property Height() As Unit
        Get
            Return _height
        End Get
        Set(ByVal Value As Unit)
            If Not Value.Type = UnitType.Pixel Then
                Throw New ArgumentException("Height must be given in
pixels.")
            End If
            _height = Value
        End Set
    End Property 'Height

    <Browsable(False)> _
    Public Property SelectedIndex() As Integer
        Get
            Return _selectedIndex
        End Get
        Set(ByVal Value As Integer)
            _selectedIndex = Value
        End Set
    End Property 'SelectedIndex

    <Category("Behavior"), Description("Whether or not the control
posts back when you change tabs.")> _
    Public Property AutoPostBack() As Boolean
        Get
            Return _autoPostBack
        End Get
        Set(ByVal Value As Boolean)
            _autoPostBack = Value
        End Set
    End Property 'AutoPostBack

    <Category("Appearance"), Description("The font-family of the tab
text.")> _
    Public Property TabFontFamily() As String
        Get
            Return _tabFontFamily
        End Get
        Set(ByVal Value As String)
            If Value Is Nothing Then
                Value = "System"
            End If
            _tabFontFamily = Value
        End Set
    End Property 'TabFontFamily

    <Category("Appearance"), Description("The font-size of the tab
text.")> _
    Public Property TabFontSize() As FontUnit
        Get
            Return _tabFontSize
        End Get
        Set(ByVal Value As FontUnit)
            If Value.IsEmpty Then
                Value = FontUnit.Point(8)
            End If
            _tabFontSize = Value
        End Set
    End Property 'TabFontSize

#End Region

#Region "WebControl Properties to Hide"

    <Browsable(False)> _
    Public Shadows ReadOnly Property Visible() As Boolean
        Get
            Return True
        End Get
    End Property 'Visible

    <Browsable(False)> _
    Public Shadows ReadOnly Property ForeColor() As
System.Drawing.Color
        Get
            Return System.Drawing.Color.Empty
        End Get
    End Property 'ForeColor

    <Browsable(False)> _
    Public Shadows ReadOnly Property BackColor() As
System.Drawing.Color
        Get
            Return System.Drawing.Color.Empty
        End Get
    End Property 'BackColor

    <Browsable(False)> _
    Public Shadows ReadOnly Property BorderColor() As
System.Drawing.Color
        Get
            Return System.Drawing.Color.Empty
        End Get
    End Property 'BorderColor

    <Browsable(False)> _
    Public Shadows ReadOnly Property BorderStyle() As BorderStyle
        Get
            Return Nothing
        End Get
    End Property 'BorderStyle

    <Browsable(False)> _
    Public Shadows ReadOnly Property BorderWidth() As Unit
        Get
            Return Nothing
        End Get
    End Property 'BorderWidth

    <Browsable(False)> _
    Public Shadows ReadOnly Property CssClass() As String
        Get
            Return String.Empty
        End Get
    End Property 'CssClass

    <Browsable(False)> _
    Public Shadows ReadOnly Property Font() As System.Drawing.Font
        Get
            Return Nothing
        End Get
    End Property 'Font

    <Browsable(False)> _
    Public Shadows ReadOnly Property EnableViewState() As Boolean
        Get
            Return True
        End Get
    End Property 'EnableViewState

    <Browsable(False)> _
    Public Shadows ReadOnly Property AccessKey() As String
        Get
            Return String.Empty
        End Get
    End Property 'AccessKey

#End Region

    Protected Overrides Sub OnInit(ByVal e As EventArgs)
        MyBase.OnInit(e)
        If AutoPostBack And Not (Page Is Nothing) Then
            Page.RegisterRequiresPostBack(Me)
        End If
    End Sub 'OnInit

    Protected Overrides Sub LoadViewState(ByVal viewState As Object)
        If Not (viewState Is Nothing) And _autoPostBack Then
            _selectedIndex = Int32.Parse(CType(viewState, String))
        Else
            MyBase.LoadViewState(viewState)
        End If
    End Sub 'LoadViewState

    Protected Overrides Function SaveViewState() As Object
        ' If AutoPostBack is set, save the SelectedTab to the view
state for postback scenarios
        If _autoPostBack Then
            Return _selectedIndex.ToString()
        Else
            Return MyBase.SaveViewState()
        End If
    End Function 'SaveViewState

#Region "Overriden methods"

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)

        'this script is client script and should appear only once
        If Not Page.IsClientScriptBlockRegistered("TabControl_vbs")
Then
            Dim reader As New
System.IO.StreamReader(Me.GetType().Assembly.GetManifestResourceStream(Me.GetType(),
"TabControl.vbs"))
            Dim script As String = "<script language='vbscript'
type='text/vbscript' >" _
                                + ControlChars.CrLf _
                                + "<!--" _
                                + ControlChars.CrLf _
                                + reader.ReadToEnd() _
                                + ControlChars.CrLf _
                                + "//-->" _
                                + ControlChars.CrLf _
                                + "</script>"
            Page.RegisterClientScriptBlock("TabControl_vbs", script)

            reader = Nothing
            script = Nothing
        End If

    End Sub 'OnPreRender

    Private Function GetSelectedIndexForRender() As Integer

        If Tabs.Count > 0 Then
            ' Get the Selected Index.
            ' For a non-AutoPostBack TabControl, the selected index
            ' is stored in a cookie.
            ' Otherwise, it is stored as a property of the TabControl.
            If Not _autoPostBack And Not (Page Is Nothing) Then
                _selectedIndex = 0
                Dim cookie As System.Web.HttpCookie =
Page.Request.Cookies((Me.UniqueID + "_SelectedIndex"))
                If Not (cookie Is Nothing) Then
                    _selectedIndex = Int32.Parse(cookie.Value)
                End If
                If _selectedIndex < 0 Or _selectedIndex > Tabs.Count -
1 Then
                    _selectedIndex = 0
                End If
            End If

            'if the selected tab isn't enabled, it can't be selected
            If Tabs(_selectedIndex).Enabled Then
                Return _selectedIndex
            Else
                _selectedIndex = -1
                'get the first tab that's enabled and select it
                For Each myTab As Tab In Tabs
                    If myTab.Enabled Then
                        Return Tabs.IndexOf(myTab)
                        Exit For
                    End If
                Next
            End If
        End If

    End Function 'GetSelectedIndexForRender

    Private Sub GetTabFormatValuesForRender()

        Dim _tabCount As Integer = Tabs.Count

        'get the number of tabs in full rows and their widths
        _tabsInFullRow = TabsPerRow
        _fullRowTabWidth = Unit.Pixel(Math.Floor(Width.Value /
CDbl(_tabsInFullRow)))

        'get the number of rows, total
        _tabRows = CType(Math.Ceiling(CDbl(_tabCount) /
CDbl(_tabsInFullRow)), Integer)

        'get the number of tabs in a partial row (a row with fewer than
TabsPerRow tabs)
        If _tabRows * _tabsInFullRow = _tabCount Then
            _fullRows = _tabRows
        Else
            _fullRows = _tabRows - 1
        End If
        _tabsInPartRow = _tabCount - (_fullRows * _tabsInFullRow)

        'get the widths of tabs in a partial row
        If _tabsInPartRow > 0 Then
            _partRowTabWidth = Unit.Pixel(Math.Floor(Width.Value /
CDbl(_tabsInPartRow)))
        Else
            _partRowTabWidth = _fullRowTabWidth
        End If

        'but just in case they don't divide roundly, we need the
remainders
        _fullRowRemainder = Width.Value - _fullRowTabWidth.Value *
_tabsInFullRow
        _partRowRemainder = Width.Value - _partRowTabWidth.Value *
_tabsInPartRow

        'figure out the height of the masterPage
        _tabPageHeight = Unit.Pixel(Height.Value - (_tabRows *
TabHeight.Value))

        'let's make a jagged array that represents the tabs in their
rows
        'we'll put the tab widths in Pair.Second
        ReDim _rowArray(_tabRows - 1)
        For i As Integer = 0 To _tabRows - 1
            If _fullRows < _tabRows And i = 0 Then
                ReDim _rowArray(i)(_tabsInPartRow - 1)
                For j As Integer = 0 To _tabsInPartRow - 1
                    If j = 0 Then
                        'add in the remainder
                        _rowArray(i)(j) = New Pair(-5,
Unit.Pixel(_partRowTabWidth.Value + _partRowRemainder))
                    Else
                        _rowArray(i)(j) = New Pair(-4,
_partRowTabWidth)
                    End If
                Next
            Else
                ReDim _rowArray(i)(_tabsInFullRow - 1)
                For j As Integer = 0 To _tabsInFullRow - 1
                    If j = 0 Then
                        'add in the remainder
                        _rowArray(i)(j) = New Pair(-3,
Unit.Pixel(_fullRowTabWidth.Value + _fullRowRemainder))
                    Else
                        _rowArray(i)(j) = New Pair(-2,
_fullRowTabWidth)
                    End If
                Next
            End If
        Next

        'now let's fill that array with tab indices (that goes into
Pair.First)
        Dim _tabCollectionCounter As Integer = 0
        Dim _tabCounter As Integer = 0
        Dim _rowCounter As Integer = _tabRows - 1

        _tabCount = Tabs.Count

        Do While _tabCollectionCounter < _tabCount
            _rowArray(_rowCounter)(_tabCounter).First =
_tabCollectionCounter
            If _tabCollectionCounter = _selectedIndex Then
                _selectedRow = _rowCounter
            End If
            _tabCounter = _tabCounter + 1
            If _tabCounter = _tabsInFullRow Then
                _rowCounter = _rowCounter - 1
                _tabCounter = 0
            End If

            _tabCollectionCounter = _tabCollectionCounter + 1
        Loop

    End Sub 'GetTabFormatValuesForRender

    Private Sub ApplyChildPostBack()

        'Populate controls with PostData, saving a list of those that
were modified:
        Dim IsChanged As Boolean
        Dim requestForm As New
System.Collections.Specialized.NameValueCollection(Page.Request.Form)
        Dim propertyChangedList As String = requestForm(Me.ClientID &
":PropertyChangedList")
        If propertyChangedList Is Nothing Then
            Exit Sub
        End If

        Dim changedControls() As String =
propertyChangedList.Split(",")
        Dim modifiedControls As New ArrayList
        For Each myTab As Tab In Tabs
            For Each myControl As Control In myTab.Controls
                If TypeOf myControl Is IPostBackDataHandler Then
                    'If a property has changed on a control, update it
                    IsChanged = False
                    For i As Integer = 0 To changedControls.Length - 1
                        If changedControls(i) = myControl.ID Then
                            IsChanged = True
                            Exit For
                        End If
                    Next
                    If IsChanged Then
                        If CType(myControl,
IPostBackDataHandler).LoadPostData(myControl.ID, requestForm) Then
                            modifiedControls.Add(myControl)
                        End If
                    End If
                End If
            Next myControl
        Next myTab

        'Raise PostDataChanged event on all modified controls:
        Dim modifiedControl As IPostBackDataHandler
        For Each modifiedControl In modifiedControls
            modifiedControl.RaisePostDataChangedEvent()
        Next modifiedControl

    End Sub 'ApplyChildPostBack

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()
        _propertyChangedList = New DropDownList
        _propertyChangedList.ID = "PropertyChangedList"
        _propertyChangedList.Attributes.Add("style", "display:none")
        Controls.Add(_propertyChangedList)
    End Sub 'CreateChildControls

    Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)

        'if there is no usable selectedindex, don't render the control
        If GetSelectedIndexForRender() = -1 Then
            Exit Sub
        End If

        'get the arrangement and width of tabs
        GetTabFormatValuesForRender()

        'a kludgy thing to get around the fact that the contents of
tabs
        'aren't seen by the page, and therefore aren't updated
        ApplyChildPostBack()

        'now let's do the rendering
        writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0",
False)
        writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0",
False)
        writer.AddAttribute(HtmlTextWriterAttribute.Border, "0", False)
        writer.AddAttribute(HtmlTextWriterAttribute.Width,
Width.ToString, False)
        writer.RenderBeginTag(HtmlTextWriterTag.Table)

        'first do the rows other than the selected row
        For i As Integer = 0 To _rowArray.GetUpperBound(0)
            If Not i = _selectedRow Then
                writer.RenderBeginTag(HtmlTextWriterTag.Tr)

                writer.AddAttribute(HtmlTextWriterAttribute.Nowrap,
"true", False)
                writer.AddAttribute(HtmlTextWriterAttribute.Valign,
"middle", False)
                writer.RenderBeginTag(HtmlTextWriterTag.Td)

                For j As Integer = 0 To _rowArray(i).GetUpperBound(0)
                    _tabIdx = CInt(_rowArray(i)(j).First)
                    writer.AddStyleAttribute("border-bottom", "none")
                    writer.AddStyleAttribute("height",
TabHeight.Value.ToString)
                    writer.AddStyleAttribute("overflow", "hidden")
                    writer.AddStyleAttribute("text-align", "center")
                    writer.AddStyleAttribute("text-align", "center")
                    writer.AddStyleAttribute("text-align", "center")
                    writer.AddStyleAttribute("font-family",
TabFontFamily)
                    writer.AddStyleAttribute("font-size",
TabFontSize.ToString)
                    writer.AddStyleAttribute("display", "inline")
                    writer.AddStyleAttribute("padding-top", "3px")
                    writer.AddStyleAttribute("border-left", "3px
outset")
                    writer.AddStyleAttribute("border-top", "3px
outset")
                    writer.AddStyleAttribute("border-right", "3px
outset")
                    If Tabs(_tabIdx).Enabled Then
                        writer.AddStyleAttribute("cursor", "hand")
                        If Not Tabs(_tabIdx).ToolTip.Trim = "" Then
                            writer.AddAttribute("title",
Tabs(_tabIdx).ToolTip, True)
                        End If
                        'set the onclick depending on whether
AutoPostBack is true or not
                        If _autoPostBack Then

writer.AddAttribute(HtmlTextWriterAttribute.Onclick, "jscript:" +
Page.GetPostBackEventReference(Me, _tabIdx.ToString()), False)
                        Else

writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
"TabControl_SelectTab(me)", False)
                        End If
                    Else
                        writer.AddAttribute("disabled", "true")
                    End If
                    writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
CType(_rowArray(i)(j).Second, Unit).ToString)
                    writer.AddStyleAttribute(HtmlTextWriterStyle.Color,
Tabs(_tabIdx).ForeColor.Name)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
Tabs(_tabIdx).BackColor.Name)
                    writer.AddAttribute(HtmlTextWriterAttribute.Name,
Me.ClientID & "_tab_" & _tabIdx.ToString, False)
                    writer.AddAttribute(HtmlTextWriterAttribute.Id,
Me.ClientID & "_tab_" & _tabIdx.ToString, False)

                    writer.RenderBeginTag(HtmlTextWriterTag.Div)
                    writer.Write(Tabs(_tabIdx).Text)
                    writer.RenderEndTag() 'Div
                Next

                writer.RenderEndTag() 'Td
                writer.RenderEndTag() 'Tr
            End If
        Next

        'now do the selected row
        writer.RenderBeginTag(HtmlTextWriterTag.Tr)

        writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "true",
False)
        writer.AddAttribute(HtmlTextWriterAttribute.Valign, "middle",
False)
        writer.RenderBeginTag(HtmlTextWriterTag.Td)

        For j As Integer = 0 To
_rowArray(_selectedRow).GetUpperBound(0)
            _tabIdx = CInt(_rowArray(_selectedRow)(j).First)
            If _rowArray(_selectedRow)(j).First = _selectedIndex Then
                writer.AddStyleAttribute("border-bottom", "none")
            Else
                writer.AddStyleAttribute("border-bottom", "3px inset")
            End If
            writer.AddStyleAttribute("height",
TabHeight.Value.ToString)
            writer.AddStyleAttribute("overflow", "hidden")
            writer.AddStyleAttribute("text-align", "center")
            writer.AddStyleAttribute("font-family", TabFontFamily)
            writer.AddStyleAttribute("font-size", TabFontSize.ToString)
            writer.AddStyleAttribute("display", "inline")
            writer.AddStyleAttribute("padding-top", "3px")
            writer.AddStyleAttribute("border-left", "3px outset")
            writer.AddStyleAttribute("border-top", "3px outset")
            writer.AddStyleAttribute("border-right", "3px outset")
            If Tabs(_tabIdx).Enabled Then
                writer.AddStyleAttribute("cursor", "hand")
                If Not Tabs(_tabIdx).ToolTip.Trim = "" Then
                    writer.AddAttribute("title", Tabs(_tabIdx).ToolTip,
True)
                End If
                'set the onclick depending on whether AutoPostBack is
true or not
                If _autoPostBack Then

writer.AddAttribute(HtmlTextWriterAttribute.Onclick, "jscript:" +
Page.GetPostBackEventReference(Me, _tabIdx.ToString()), False)
                Else

writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
"TabControl_SelectTab(me)", False)
                End If
            Else
                writer.AddAttribute("disabled", "true")
            End If
            writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
CType(_rowArray(_selectedRow)(j).Second, Unit).ToString)
            writer.AddStyleAttribute(HtmlTextWriterStyle.Color,
Tabs(_tabIdx).ForeColor.Name)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
Tabs(_tabIdx).BackColor.Name)
            writer.AddAttribute(HtmlTextWriterAttribute.Name,
Me.ClientID & "_tab_" & _tabIdx.ToString, False)
            writer.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID
& "_tab_" & _tabIdx.ToString, False)
            writer.RenderBeginTag(HtmlTextWriterTag.Div)
            writer.Write(Tabs(_tabIdx).Text)
            writer.RenderEndTag() 'Div
        Next

        writer.RenderEndTag() 'Td
        writer.RenderEndTag() 'Tr

        'so much for the tabs.  Now the tab pages/panels
        writer.RenderBeginTag(HtmlTextWriterTag.Tr)
        writer.RenderBeginTag(HtmlTextWriterTag.Td)

        writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
Tabs(_selectedIndex).BackColor.Name)
        writer.AddStyleAttribute("overflow", "auto")
        writer.AddStyleAttribute("border-left", "3px outset")
        writer.AddStyleAttribute("border-bottom", "3px outset")
        writer.AddStyleAttribute("border-right", "3px outset")
        writer.AddStyleAttribute("display", "inline")
        writer.AddStyleAttribute(HtmlTextWriterStyle.Height,
_tabPageHeight.ToString)
        writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
Width.ToString)
        writer.RenderBeginTag(HtmlTextWriterTag.Div)

        For Each myTab As Tab In Tabs
            ' Only render the TabView under the following conditions:
            ' (1) AutoPostBack is set to false.
            ' (2) AutoPostBack is set to true, and the TabView is for
the Selected Tab
            If Not _autoPostBack Or (_autoPostBack And _selectedIndex =
_tabs.IndexOf(myTab)) Then
                writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
myTab.InnerWidth.ToString)
                writer.AddStyleAttribute(HtmlTextWriterStyle.Height,
myTab.InnerHeight.ToString)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
myTab.BackColor.Name)
                writer.AddStyleAttribute(HtmlTextWriterStyle.Color,
myTab.ForeColor.Name)
                writer.AddStyleAttribute("text-align", "center")
                writer.AddAttribute(HtmlTextWriterAttribute.Id,
Me.ClientID & "_panel_" & Tabs.IndexOf(myTab).ToString, False)
                writer.AddAttribute(HtmlTextWriterAttribute.Name,
Me.ClientID & "_panel_" & Tabs.IndexOf(myTab).ToString, False)
                If _selectedIndex = _tabs.IndexOf(myTab) Then
                    writer.AddStyleAttribute("display", "inline")
                Else
                    writer.AddStyleAttribute("display", "none")
                End If
                writer.RenderBeginTag(HtmlTextWriterTag.Div)

                If myTab.HasControls() = False Then
                    myTab.Controls.Add(New LiteralControl("&nbsp;"))
                End If

                For Each myControl As Control In myTab.Controls
                    'this is so that we can update property changes on
postback
                    If TypeOf myControl Is IPostBackDataHandler Then
                        writer.AddAttribute("onpropertychange",
"TabControl_FlagChange '" & myControl.ClientID & "', '" &
_propertyChangedList.ClientID & "'")
                    End If
                    myControl.RenderControl(writer)
                Next

                writer.RenderEndTag() 'Div
            End If
        Next

        writer.RenderEndTag() 'Div
        'this is so that we can update property changes on postback
        _propertyChangedList.RenderControl(writer)
        writer.RenderEndTag() 'Td
        writer.RenderEndTag() 'Tr
        writer.RenderEndTag() 'Table

    End Sub 'Render

#End Region

    Public Function LoadPostData(ByVal postDataKey As String, ByVal
postCollection As System.Collections.Specialized.NameValueCollection)
As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData
        ' Stub Implementation, Required for IPostBackDataHandler
        ' This control must derive from IPostBackDataHandler, even
though it doesn't use its methods.
    End Function 'LoadPostData

    Public Sub RaisePostDataChangedEvent() Implements
System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent
        ' Stub Implementation, Required for IPostBackDataHandler
        ' This control must derive from IPostBackDataHandler, even
though it doesn't use its methods.
    End Sub 'RaisePostDataChangedEvent

    Public Sub RaisePostBackEvent(ByVal eventArgument As String)
Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
        If eventArgument Is Nothing Then
            Return
        End If
        _selectedIndex = Int32.Parse(eventArgument)
        Dim e As New EventArgs
        OnSelectedIndexChanged(e)
    End Sub 'RaisePostBackEvent

    Public Event SelectedIndexChanged As EventHandler

    Public Overridable Sub OnSelectedIndexChanged(ByVal e As EventArgs)
        RaiseEvent SelectedIndexChanged(Me, e)
    End Sub 'OnSelectedIndexChanged

    Protected Overrides Function OnBubbleEvent(ByVal source As Object,
ByVal args As System.EventArgs) As Boolean
        Return False
    End Function 'OnBubbleEvent

End Class 'TabControl

<ToolboxItem(False), ParseChildren(False), PersistChildren(True)> _
Public Class Tab
    Inherits WebControl

    Private _text As String
    Private _innerHeight As Unit
    Private _innerWidth As Unit
    Private _id As String

    Public Sub New()
        Me.Text = "Tab"
        Me.InnerHeight = Unit.Percentage(100)
        Me.InnerWidth = Unit.Percentage(100)
        Me.Enabled = True
        Me.ForeColor = System.Drawing.Color.Black
        Me.BackColor = System.Drawing.Color.Silver
    End Sub

#Region "Properties"

    <Browsable(True), Category("Appearance"), Description("The text
appearing on the tab.")> _
    Public Property Text() As String
        Get
            Return _text
        End Get
        Set(ByVal Value As String)
            _text = Value
        End Set
    End Property 'Text

    <Browsable(True), Category("Appearance"), Description("The height
of the content of the tab panel (can be greater or less than the height
of the panel itself).")> _
    Public Property InnerHeight() As Unit
        Get
            Return _innerHeight
        End Get
        Set(ByVal Value As Unit)
            _innerHeight = Value
        End Set
    End Property 'InnerHeight

    <Browsable(True), Category("Appearance"), Description("The width of
the content of the tab panel (can be greater or less than the width of
the panel itself).")> _
    Public Property InnerWidth() As Unit
        Get
            Return _innerWidth
        End Get
        Set(ByVal Value As Unit)
            _innerWidth = Value
        End Set
    End Property 'InnerWidth

    <Browsable(False)> _
    Public Overrides Property ID() As String
        Get
            Return _id
        End Get
        Set(ByVal Value As String)
            _id = Value
        End Set
    End Property

#End Region

#Region "WebControl Properties to Hide"

    <Browsable(False)> _
    Public Shadows ReadOnly Property Visible() As Boolean
        Get
            Return True
        End Get
    End Property 'Visible

    <Browsable(False)> _
    Public Shadows ReadOnly Property BorderColor() As
System.Drawing.Color
        Get
            Return System.Drawing.Color.Empty
        End Get
    End Property 'BorderColor

    <Browsable(False)> _
    Public Shadows ReadOnly Property BorderStyle() As BorderStyle
        Get
            Return Nothing
        End Get
    End Property 'BorderStyle

    <Browsable(False)> _
    Public Shadows ReadOnly Property BorderWidth() As Unit
        Get
            Return Nothing
        End Get
    End Property 'BorderWidth

    <Browsable(False)> _
    Public Shadows ReadOnly Property CssClass() As String
        Get
            Return String.Empty
        End Get
    End Property 'CssClass

    <Browsable(False)> _
    Public Shadows ReadOnly Property Font() As System.Drawing.Font
        Get
            Return Nothing
        End Get
    End Property 'Font

    <Browsable(False)> _
    Public Shadows ReadOnly Property Height() As Unit
        Get
            Return Unit.Empty
        End Get
    End Property 'Height

    <Browsable(False)> _
    Public Shadows ReadOnly Property Width() As Unit
        Get
            Return Unit.Empty
        End Get
    End Property 'Width

    <Browsable(False)> _
    Public Shadows ReadOnly Property EnableViewState() As Boolean
        Get
            Return True
        End Get
    End Property 'EnableViewState

    <Browsable(False)> _
    Public Shadows ReadOnly Property AccessKey() As String
        Get
            Return String.Empty
        End Get
    End Property 'AccessKey

#End Region

    Protected Overrides Function OnBubbleEvent(ByVal source As Object,
ByVal args As System.EventArgs) As Boolean
        Return False
    End Function 'OnBubbleEvent

End Class 'Tab

Public Class TabCollection
    Inherits CollectionBase

    Default Public ReadOnly Property Item(ByVal index As Integer) As
Tab
        Get
            If index < 0 Or index >= MyBase.List.Count Then
                Stop
            End If
            Return CType(MyBase.List(index), Tab)
        End Get
    End Property 'Item

    Public Sub Add(ByVal myTab As Tab)
        MyBase.List.Add(myTab)
    End Sub 'Add

    Public Function IndexOf(ByVal myTab As Tab) As Integer
        Return MyBase.List.IndexOf(myTab)
    End Function 'IndexOf

End Class 'TabCollection

Public Class TabControlDesigner
    Inherits System.Web.UI.Design.ControlDesigner

    Private verbAddTab As DesignerVerb
    Private verbShowNextTab As DesignerVerb
    Private verbRemoveTab As DesignerVerb
    Private DesignTimeSelectedIndex As Integer

    Public Sub New()
        verbAddTab = New DesignerVerb("Add New Tab", New
EventHandler(AddressOf AddTab))
        verbShowNextTab = New DesignerVerb("Show Next Tab", New
EventHandler(AddressOf ShowNextTab))
        verbRemoveTab = New DesignerVerb("Remove Current Tab", New
EventHandler(AddressOf RemoveTab))
        MyBase.Verbs.Add(verbAddTab)
        MyBase.Verbs.Add(verbShowNextTab)
        MyBase.Verbs.Add(verbRemoveTab)
    End Sub 'New

    Public Overrides Sub Initialize(ByVal component As IComponent)
        MyBase.Initialize(component)
        Dim tabControl As tabControl = CType(component, tabControl)
        DesignTimeSelectedIndex = tabControl.SelectedIndex
        Dim ss As ISelectionService =
CType(GetService(GetType(ISelectionService)), ISelectionService)
        Dim ccs As IComponentChangeService =
CType(GetService(GetType(IComponentChangeService)),
IComponentChangeService)
        If Not (ss Is Nothing) Then
            AddHandler ss.SelectionChanged, AddressOf
OnSelectionChanged
            AddHandler ccs.ComponentChanged, AddressOf
OnComponentChanged
        End If
    End Sub 'Initialize

    Protected Overloads Overrides Sub Dispose(ByVal disposing As
Boolean)
        Dim ss As ISelectionService =
CType(GetService(GetType(ISelectionService)), ISelectionService)
        Dim ccs As IComponentChangeService =
CType(GetService(GetType(IComponentChangeService)),
IComponentChangeService)
        If Not (ss Is Nothing) Then
            AddHandler ss.SelectionChanged, New EventHandler(AddressOf
OnSelectionChanged)
            AddHandler ccs.ComponentChanged, New
ComponentChangedEventHandler(AddressOf OnComponentChanged)
        End If
        MyBase.Dispose(disposing)
    End Sub 'Dispose

    Public Overrides Sub OnComponentChanged(ByVal sender As Object,
ByVal e As ComponentChangedEventArgs)
        MyBase.OnComponentChanged(sender, e)
        If e.Component Is Me.Component Then
            ModifyMenu()
        End If
    End Sub 'OnComponentChanged

    Public Overrides ReadOnly Property
DesignTimeHtmlRequiresLoadComplete() As Boolean
        Get
            Return True
        End Get
    End Property 'DesignTimeHtmlRequiresLoadComplete

    Protected Overrides Function GetEmptyDesignTimeHtml() As String
        ' Provide the developer with info on how to add tabs to the
TabControl.
        Dim strHtml As String = ""
        strHtml += "<table style='font-family: Tahoma; font-size: 8pt;
color:buttontext; background-color:buttonface; border: solid 1px
border-top-color: buttonhighlight; border-left-color: buttonhightlight;
border-right-color: buttonshadow; borderbottom-color: buttonshadow;
display: inline'>"
        strHtml += "<tr><td><b>TabControl</b> - " + Me.ID +
"</td></tr>"
        strHtml += "<tr><td>Please add Tabs through the Tabs
(Collection) property in the Properties pane,</td></tr>"
        strHtml += "<tr><td>or by clicking ""Add New Tab"" in either
the description area of the</td></tr>"
        strHtml += "<tr><td>Properties pane or the right-click
menu.</td></tr>"
        strHtml += "<tr><td>Then switch to HTML view and edit each
Tab's view by inserting inner content.</td></tr></table>"
        Return strHtml
    End Function 'GetEmptyDesignTimeHtml

    Public Overrides Function GetDesignTimeHtml() As String

        Dim tabControl As tabControl = CType(Me.Component, tabControl)
        If Not (tabControl Is Nothing) And tabControl.Tabs.Count > 0
Then
            Dim stringWriter As New stringWriter
            Dim writer As New HtmlTextWriter(stringWriter)

            Dim _tabRows As Integer
            Dim _fullRows As Integer
            Dim _tabsInFullRow As Integer = tabControl.TabsPerRow
            Dim _tabsInPartRow As Integer
            Dim _partRowTabWidth As Unit
            Dim _fullRowTabWidth As Unit
            Dim _fullRowRemainder As Integer
            Dim _partRowRemainder As Integer
            Dim _tabPageHeight As Unit
            Dim _rowArray As Pair()()
            Dim _tabIdx As Integer
            Dim _selectedRow As Integer
            Dim _autoPostBack As Boolean = tabControl.AutoPostBack
            Dim _tabHeight As Unit = tabControl.TabHeight
            Dim _tabFontFamily As String = tabControl.TabFontFamily
            Dim _tabFontSize As FontUnit = tabControl.TabFontSize
            Dim _width As Unit = tabControl.Width
            Dim _height As Unit = tabControl.Height
            Dim _tabCount As Integer = tabControl.Tabs.Count

            'get the widths of tabs in a full row
            _fullRowTabWidth = Unit.Pixel(Math.Floor(_width.Value /
CDbl(_tabsInFullRow)))

            'get the number of rows, total
            _tabRows = CType(Math.Ceiling(CDbl(_tabCount) /
CDbl(_tabsInFullRow)), Integer)

            'get the number of tabs in a partial row (a row with fewer
than TabsPerRow tabs)
            If _tabRows * _tabsInFullRow = _tabCount Then
                _fullRows = _tabRows
            Else
                _fullRows = _tabRows - 1
            End If
            _tabsInPartRow = _tabCount - (_fullRows * _tabsInFullRow)

            'get the widths of tabs in a partial row
            If _tabsInPartRow > 0 Then
                _partRowTabWidth = Unit.Pixel(Math.Floor(_width.Value /
CDbl(_tabsInPartRow)))
            Else
                _partRowTabWidth = _fullRowTabWidth
            End If

            'but just in case they don't divide roundly, we need the
remainders
            _fullRowRemainder = _width.Value - _fullRowTabWidth.Value *
_tabsInFullRow
            _partRowRemainder = _width.Value - _partRowTabWidth.Value *
_tabsInPartRow

            'figure out the height of the masterPage
            _tabPageHeight = Unit.Pixel(_height.Value - (_tabRows *
_tabHeight.Value))

            'let's make a jagged array that represents the tabs in
their rows
            'we'll put the tab widths in Pair.Second
            ReDim _rowArray(_tabRows - 1)
            For i As Integer = 0 To _tabRows - 1
                If _fullRows < _tabRows And i = 0 Then
                    ReDim _rowArray(i)(_tabsInPartRow - 1)
                    For j As Integer = 0 To _tabsInPartRow - 1
                        If j = 0 Then
                            'add in the remainder
                            _rowArray(i)(j) = New Pair(-1,
Unit.Pixel(_partRowTabWidth.Value + _partRowRemainder))
                        Else
                            _rowArray(i)(j) = New Pair(-1,
_partRowTabWidth)
                        End If
                    Next
                Else
                    ReDim _rowArray(i)(_tabsInFullRow - 1)
                    For j As Integer = 0 To _tabsInFullRow - 1
                        If j = 0 Then
                            'add in the remainder
                            _rowArray(i)(j) = New Pair(-1,
Unit.Pixel(_fullRowTabWidth.Value + _fullRowRemainder))
                        Else
                            _rowArray(i)(j) = New Pair(-1,
_fullRowTabWidth)
                        End If
                    Next
                End If
            Next

            'now let's fill that array with tab indices (that goes into
Pair.First)
            Dim _tabCollectionCounter As Integer = 0
            Dim _tabCounter As Integer = 0
            Dim _rowCounter As Integer = _tabRows - 1

            Do While _tabCollectionCounter < _tabCount
                _rowArray(_rowCounter)(_tabCounter).First =
_tabCollectionCounter
                If _tabCollectionCounter = DesignTimeSelectedIndex Then
                    _selectedRow = _rowCounter
                End If
                _tabCounter = _tabCounter + 1
                If _tabCounter = _tabsInFullRow Then
                    _rowCounter = _rowCounter - 1
                    _tabCounter = 0
                End If

                _tabCollectionCounter = _tabCollectionCounter + 1
            Loop

            'now let's do the rendering
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding,
"0", False)
            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing,
"0", False)
            writer.AddAttribute(HtmlTextWriterAttribute.Border, "0",
False)
            writer.AddAttribute(HtmlTextWriterAttribute.Width,
_width.ToString, False)
            writer.RenderBeginTag(HtmlTextWriterTag.Table)

            'first do all the rows other than the selected row
            For i As Integer = 0 To _rowArray.GetUpperBound(0)
                If Not i = _selectedRow Then
                    writer.RenderBeginTag(HtmlTextWriterTag.Tr)

                    writer.AddAttribute(HtmlTextWriterAttribute.Nowrap,
"true", False)
                    writer.AddAttribute(HtmlTextWriterAttribute.Valign,
"middle", False)
                    writer.RenderBeginTag(HtmlTextWriterTag.Td)

                    For j As Integer = 0 To
_rowArray(i).getUpperBound(0)
                        _tabIdx = CInt(_rowArray(i)(j).First)
                        writer.AddStyleAttribute("border-bottom",
"none")
                        writer.AddStyleAttribute("height",
_tabHeight.Value.ToString)
                        writer.AddStyleAttribute("overflow", "hidden")
                        writer.AddStyleAttribute("text-align",
"center")
                        writer.AddStyleAttribute("font-family",
_tabFontFamily)
                        writer.AddStyleAttribute("font-size",
_tabFontSize.ToString)
                        writer.AddStyleAttribute("display", "inline")
                        writer.AddStyleAttribute("padding-top", "3px")
                        writer.AddStyleAttribute("border-left", "3px
outset")
                        writer.AddStyleAttribute("border-top", "3px
outset")
                        writer.AddStyleAttribute("border-right", "3px
outset")
                        writer.AddStyleAttribute("cursor", "hand")

writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
CType(_rowArray(i)(j).Second, Unit).ToString)

writer.AddStyleAttribute(HtmlTextWriterStyle.Color,
tabControl.Tabs(_tabIdx).ForeColor.Name)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
tabControl.Tabs(_tabIdx).BackColor.Name)

                        writer.RenderBeginTag(HtmlTextWriterTag.Div)
                        writer.Write(tabControl.Tabs(_tabIdx).Text)
                        writer.RenderEndTag() 'Div
                    Next

                    writer.RenderEndTag() 'Td
                    writer.RenderEndTag() 'Tr
                End If
            Next

            'then do the selected row
            writer.RenderBeginTag(HtmlTextWriterTag.Tr)

            writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "true",
False)
            writer.AddAttribute(HtmlTextWriterAttribute.Valign,
"middle", False)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)

            For j As Integer = 0 To
_rowArray(_selectedRow).GetUpperBound(0)
                _tabIdx = CInt(_rowArray(_selectedRow)(j).First)
                If _rowArray(_selectedRow)(j).First =
DesignTimeSelectedIndex Then
                    writer.AddStyleAttribute("border-bottom", "none")
                Else
                    writer.AddStyleAttribute("border-bottom", "3px
inset")
                End If
                writer.AddStyleAttribute("height",
_tabHeight.Value.ToString)
                writer.AddStyleAttribute("overflow", "hidden")
                writer.AddStyleAttribute("text-align", "center")
                writer.AddStyleAttribute("font-family", _tabFontFamily)
                writer.AddStyleAttribute("font-size",
_tabFontSize.ToString)
                writer.AddStyleAttribute("display", "inline")
                writer.AddStyleAttribute("padding-top", "3px")
                writer.AddStyleAttribute("border-left", "3px outset")
                writer.AddStyleAttribute("border-top", "3px outset")
                writer.AddStyleAttribute("border-right", "3px outset")
                writer.AddStyleAttribute("cursor", "hand")
                writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
CType(_rowArray(_selectedRow)(j).Second, Unit).ToString)
                writer.AddStyleAttribute(HtmlTextWriterStyle.Color,
tabControl.Tabs(_tabIdx).ForeColor.Name)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
tabControl.Tabs(_tabIdx).BackColor.Name)

                writer.RenderBeginTag(HtmlTextWriterTag.Div)
                writer.Write(tabControl.Tabs(_tabIdx).Text)
                writer.RenderEndTag() 'Div
            Next

            writer.RenderEndTag() 'Td
            writer.RenderEndTag() 'Tr

            'so much for the tabs.  Now the tab pages/panels
            writer.RenderBeginTag(HtmlTextWriterTag.Tr)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)


writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
tabControl.Tabs(tabControl.SelectedIndex).BackColor.Name)
            writer.AddStyleAttribute("overflow", "auto")
            writer.AddStyleAttribute("border-left", "3px outset")
            writer.AddStyleAttribute("border-bottom", "3px outset")
            writer.AddStyleAttribute("border-right", "3px outset")
            writer.AddStyleAttribute("display", "inline")
            writer.AddStyleAttribute(HtmlTextWriterStyle.Height,
_tabPageHeight.ToString)
            writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
_width.ToString)
            writer.RenderBeginTag(HtmlTextWriterTag.Div)

            For Each myTab As Tab In tabControl.Tabs
                ' Only render the TabView under the following
conditions:
                ' (1) AutoPostBack is set to false.
                ' (2) AutoPostBack is set to true, and the TabView is
for the Selected Tab
                If DesignTimeSelectedIndex =
tabControl.Tabs.IndexOf(myTab) Then
                    writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
myTab.InnerWidth.ToString)

writer.AddStyleAttribute(HtmlTextWriterStyle.Height,
myTab.InnerHeight.ToString)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
myTab.BackColor.Name)
                    writer.AddStyleAttribute(HtmlTextWriterStyle.Color,
myTab.ForeColor.Name)
                    writer.AddStyleAttribute("text-align", "center")
                    writer.RenderBeginTag(HtmlTextWriterTag.Div)

                    If myTab.HasControls() = False Then
                        myTab.Controls.Add(New
LiteralControl("&nbsp;"))
                    End If


                    For Each myControl As Control In myTab.Controls
                        myControl.RenderControl(writer)
                    Next

                    writer.RenderEndTag() 'Div
                End If
            Next

            writer.RenderEndTag() 'Div
            writer.RenderEndTag() 'Td
            writer.RenderEndTag() 'Tr
            writer.RenderEndTag() 'Table

            Return stringWriter.ToString()
        Else
            Return Me.GetEmptyDesignTimeHtml()
        End If

    End Function 'GetDesignTimeHtml

    Private Sub OnSelectionChanged(ByVal sender As Object, ByVal e As
EventArgs)
        Dim ss As ISelectionService = CType(sender, ISelectionService)
        If Not (ss Is Nothing) Then
            Dim bTabControlSelected As Boolean =
ss.GetComponentSelected(Me.Component)
            If bTabControlSelected Then
                ModifyMenu()
            End If
        End If
    End Sub 'OnSelectionChanged

    Private Sub ModifyMenu()
        Dim tabControl As tabControl = CType(Me.Component, tabControl)
        verbShowNextTab.Visible = False
        verbRemoveTab.Visible = False
        verbShowNextTab.Visible = tabControl.Tabs.Count > 1
        verbRemoveTab.Visible = tabControl.Tabs.Count > 0
    End Sub 'ModifyMenu

    Private Sub AddTab(ByVal sender As Object, ByVal e As EventArgs)
        Dim tabControl As tabControl = CType(Me.Component, tabControl)
        Dim myTab As New Tab
        tabControl.Tabs.Add(myTab)
        tabControl.Controls.Add(myTab)
        myTab.Text = "Tab " & tabControl.Tabs.Count.ToString
        myTab.ID = myTab.ClientID
        DesignTimeSelectedIndex = tabControl.Tabs.Count - 1
        OnComponentChanged(Me, New
ComponentChangedEventArgs(tabControl, Nothing, Nothing, Nothing))
    End Sub 'AddTab

    Private Sub ShowNextTab(ByVal sender As Object, ByVal e As
EventArgs)
        Dim tabControl As tabControl = CType(Me.Component, tabControl)
        Dim TabCount As Integer = tabControl.Tabs.Count
        If TabCount > 0 Then
            DesignTimeSelectedIndex = (DesignTimeSelectedIndex + 1) Mod
TabCount
            UpdateDesignTimeHtml()
        End If
    End Sub 'ShowNextTab

    Private Sub RemoveTab(ByVal sender As Object, ByVal e As EventArgs)
        Dim tabControl As tabControl = CType(Me.Component, tabControl)
        If tabControl.Tabs.Count > 0 Then
            tabControl.Tabs.RemoveAt(DesignTimeSelectedIndex)
            ' Ensure the selected index is still valid.
            ' It is possible that the selected Tab was the last one,
            ' and that one was removed.
            If tabControl.SelectedIndex >= tabControl.Tabs.Count Then
                tabControl.SelectedIndex = tabControl.Tabs.Count - 1
                If tabControl.SelectedIndex < 0 Then
                    tabControl.SelectedIndex = 0
                End If
            End If
            ' Commit the Change
            OnComponentChanged(Me, New
ComponentChangedEventArgs(tabControl, Nothing, Nothing, Nothing))
            ' Show the closest, right-most sibling Tab in the Designer
            DesignTimeSelectedIndex -= 1
            If DesignTimeSelectedIndex + 1 >= tabControl.Tabs.Count
Then
                DesignTimeSelectedIndex -= 1
            End If
            ShowNextTab(Me, EventArgs.Empty)
        End If
    End Sub 'RemoveTab

End Class 'TabControlDesigner


Brock Allen wrote:
Show quoteHide quote
> Post your code, as that will help in determining what's going on.
>
> -Brock
> DevelopMentor
> http://staff.develop.com/ballen
>
>
>
> > I have a control which contains a collection of another control.
The
> > members of the collection can contain child controls themselves,
but
> > these aren't coded.  They are merely added between the start and
end
> > tags of the child controls in the HTML view.
> >
> > If I put a button (for instance) inside one of the child controls,
the
> > button's click event causes a postback, but then seems to disappear
> > entirely.  I tried looking at OnBubbleEvent in the child control
and
> > in the parent control.  Those events are never called.  Nor does
the
> > button's click event in the page containing the control ever get
> > called.
> >
> > Is there a way to capture events of such child controls?  I get the
> > feeling that these child controls aren't being added to the control
> > hierarchy of the parent control, but they are visible when I do
> > ParentControl.ChildrenControls(idx).Controls(idx2), so maybe they
are
> > in the control hierarchy.
> >
> > I'm completely stumped here, and I'd appreciate any suggestions.
> >
> > TIA,
> > Lisa
Author
10 May 2005 7:46 PM
Brock Allen
> It's a bit long, but here it is.  I appreciate the help.

Ugg, I should have said "post the smallest amount of code that reproduces
the problem".

Ok, so I skimmed the code and, IIRC, your problem is that declared controls
aren't getting server side events called? I think past of the problem is
that the controls aren't part of the control hierarchy... meaning, I don't
see where the controls that are in the TabdCollection are ever added into
the control tree. If they're not in the control tree then they miss out on
a lot of the server side processing. Again, that was a huge code dump, so
my quick glance at it might be off.

-Brock
DevelopMentor
http://staff.develop.com/ballen
Author
10 May 2005 9:18 PM
lisa
Brock Allen wrote:
> > It's a bit long, but here it is.  I appreciate the help.
>
> Ugg, I should have said "post the smallest amount of code that
reproduces
> the problem".

I'm sorry.  I honestly wasn't sure what part that would be.  Although I
suppose I could have left out the designer. <sheepish grin>

> Ok, so I skimmed the code and, IIRC, your problem is that declared
controls
> aren't getting server side events called? I think past of the problem
is
> that the controls aren't part of the control hierarchy... meaning, I
don't
> see where the controls that are in the TabdCollection are ever added
into
> the control tree. If they're not in the control tree then they miss
out on
> a lot of the server side processing. Again, that was a huge code
dump, so
> my quick glance at it might be off.

I apologize for the size, Brock.  Here is what it looks like in the
HTML at design time:

  <fw:TabControl id="TabControl1" runat="server">
    <fw:Tab ID="TabControl1__ctl0" Text="Tab 1">&nbsp;</fw:Tab>
    <fw:Tab ID="TabControl1__ctl1" Text="Tab 2">
      <asp:Button id="Button1" runat="server" Text="Button" />
    </fw:Tab>
  </fw:TabControl>

When I dragged it onto the form and added two tabs, it looked just like
this, except that there was a non-breaking space where the button is.

When I'm in run time, I see this in the Command Window:

  ?me.Tabs(1).Controls(1).ID
  "Button1"

"Me", in this case, is the TabControl.  Maybe I don't understand the
control hierarchy, but doesn't the fact that I can reference the button
at run time this way mean that the button is part of the control
hierarchy?

In terms of how the Tabs themselves are added, the TabCollection has
this:

  Public Sub Add(ByVal myTab As Tab)
    MyBase.List.Add(myTab)
  End Sub 'Add

And the TabControl has this in global declarations:

  Private _tabs As New TabCollection

And it has this property:

  Public ReadOnly Property Tabs() As TabCollection
    Get
      Return _tabs
    End Get
  End Property

Assuming that that's all the relevant code (which I'm unsure of), do
you see what could be causing my problem?  Since the Button is already
visible at run time as a child of the second tab, I don't know what I
could add the Button to without it creating a second Button.

The Microsoft Multipage control works the same way.  I can add a Button
(let's say) between the start and end tags of a PageView control
(analogous to my Tab control), and somehow, the Button's events are
called properly.  The biggest difference I can see is that their
PageViewCollection is derived from ControlCollection, while my
TabCollection is derived from CollectionBase.  But I can't see how that
could be causing the problems.

I really appreciate the help, Brock.

Lisa
Author
12 May 2005 3:06 PM
lisa
I'm sorry to bother you, but I was wondering if you'd had a chance to
take a look at this.

Thanks,
Lisa


l***@starways.net wrote:
Show quoteHide quote
> Brock Allen wrote:
> > > It's a bit long, but here it is.  I appreciate the help.
> >
> > Ugg, I should have said "post the smallest amount of code that
> > reproduces the problem".
>
> I'm sorry.  I honestly wasn't sure what part that would be.  Although
I
> suppose I could have left out the designer. <sheepish grin>
>
> > Ok, so I skimmed the code and, IIRC, your problem is that declared
> > controls aren't getting server side events called? I think past of
> > the problem is that the controls aren't part of the control
> > hierarchy... meaning, I don't see where the controls that are in
the
> > TabdCollection are ever added into the control tree. If they're not

> > in the control tree then they miss out on a lot of the server side
> > processing. Again, that was a huge code dump, so my quick glance at

> > it might be off.
>
> I apologize for the size, Brock.  Here is what it looks like in the
> HTML at design time:
>
>   <fw:TabControl id="TabControl1" runat="server">
>     <fw:Tab ID="TabControl1__ctl0" Text="Tab 1">&nbsp;</fw:Tab>
>     <fw:Tab ID="TabControl1__ctl1" Text="Tab 2">
>       <asp:Button id="Button1" runat="server" Text="Button" />
>     </fw:Tab>
>   </fw:TabControl>
>
> When I dragged it onto the form and added two tabs, it looked just
like
> this, except that there was a non-breaking space where the button is.
>
> When I'm in run time, I see this in the Command Window:
>
>   ?me.Tabs(1).Controls(1).ID
>   "Button1"
>
> "Me", in this case, is the TabControl.  Maybe I don't understand the
> control hierarchy, but doesn't the fact that I can reference the
button
> at run time this way mean that the button is part of the control
> hierarchy?
>
> In terms of how the Tabs themselves are added, the TabCollection has
> this:
>
>   Public Sub Add(ByVal myTab As Tab)
>     MyBase.List.Add(myTab)
>   End Sub 'Add
>
> And the TabControl has this in global declarations:
>
>   Private _tabs As New TabCollection
>
> And it has this property:
>
>   Public ReadOnly Property Tabs() As TabCollection
>     Get
>       Return _tabs
>     End Get
>   End Property
>
> Assuming that that's all the relevant code (which I'm unsure of), do
> you see what could be causing my problem?  Since the Button is
already
> visible at run time as a child of the second tab, I don't know what I
> could add the Button to without it creating a second Button.
>
> The Microsoft Multipage control works the same way.  I can add a
Button
> (let's say) between the start and end tags of a PageView control
> (analogous to my Tab control), and somehow, the Button's events are
> called properly.  The biggest difference I can see is that their
> PageViewCollection is derived from ControlCollection, while my
> TabCollection is derived from CollectionBase.  But I can't see how
that
> could be causing the problems.
>
> I really appreciate the help, Brock.
>
> Lisa
Author
12 May 2005 6:35 PM
Brock Allen
Sorry Lisa, I can't quite tell exactly what the problem is from this. What
I'd suggest is to build a small sample outside of your current project that
works the way you want it and then try to compare the differences. Here are
some samples that might get you started:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcustomizingcontrolswithtemplates.asp?frame=true

http://winfx.msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_aspnetctrlauth/html/986f63b8-6b50-42b9-a62d-a2f13cafa88b.asp

And more of the same here:

http://samples.gotdotnet.com/quickstart/aspplus/default.aspx?url=%2fquickstart%2faspplus%2fdoc%2fwebctrlauthoring.aspx

-Brock
DevelopMentor
http://staff.develop.com/ballen



Show quoteHide quote
> Brock Allen wrote:
>
>>> It's a bit long, but here it is.  I appreciate the help.
>>>
>> Ugg, I should have said "post the smallest amount of code that
>>
> reproduces
>
>> the problem".
>>
> I'm sorry.  I honestly wasn't sure what part that would be.  Although
> I suppose I could have left out the designer. <sheepish grin>
>
>> Ok, so I skimmed the code and, IIRC, your problem is that declared
>>
> controls
>
>> aren't getting server side events called? I think past of the problem
>>
> is
>
>> that the controls aren't part of the control hierarchy... meaning, I
>>
> don't
>
>> see where the controls that are in the TabdCollection are ever added
>>
> into
>
>> the control tree. If they're not in the control tree then they miss
>>
> out on
>
>> a lot of the server side processing. Again, that was a huge code
>>
> dump, so
>
>> my quick glance at it might be off.
>>
> I apologize for the size, Brock.  Here is what it looks like in the
> HTML at design time:
>
> <fw:TabControl id="TabControl1" runat="server">
> <fw:Tab ID="TabControl1__ctl0" Text="Tab 1">&nbsp;</fw:Tab>
> <fw:Tab ID="TabControl1__ctl1" Text="Tab 2">
> <asp:Button id="Button1" runat="server" Text="Button" />
> </fw:Tab>
> </fw:TabControl>
> When I dragged it onto the form and added two tabs, it looked just
> like this, except that there was a non-breaking space where the button
> is.
>
> When I'm in run time, I see this in the Command Window:
>
> ?me.Tabs(1).Controls(1).ID
> "Button1"
> "Me", in this case, is the TabControl.  Maybe I don't understand the
> control hierarchy, but doesn't the fact that I can reference the
> button at run time this way mean that the button is part of the
> control hierarchy?
>
> In terms of how the Tabs themselves are added, the TabCollection has
> this:
>
> Public Sub Add(ByVal myTab As Tab)
> MyBase.List.Add(myTab)
> End Sub 'Add
> And the TabControl has this in global declarations:
>
> Private _tabs As New TabCollection
>
> And it has this property:
>
> Public ReadOnly Property Tabs() As TabCollection
> Get
> Return _tabs
> End Get
> End Property
> Assuming that that's all the relevant code (which I'm unsure of), do
> you see what could be causing my problem?  Since the Button is already
> visible at run time as a child of the second tab, I don't know what I
> could add the Button to without it creating a second Button.
>
> The Microsoft Multipage control works the same way.  I can add a
> Button (let's say) between the start and end tags of a PageView
> control (analogous to my Tab control), and somehow, the Button's
> events are called properly.  The biggest difference I can see is that
> their PageViewCollection is derived from ControlCollection, while my
> TabCollection is derived from CollectionBase.  But I can't see how
> that could be causing the problems.
>
> I really appreciate the help, Brock.
>
> Lisa
>
Author
13 May 2005 1:57 AM
lisa
Brock, thank you!  I didn't find the answer from these links, but they
helped me figure out what the problem was.

I think the fact that the content of the Tabs are children of the Tabs
*is* the problem.  If the application sees those controls as children
of the Tabs, and the Tabs as children of the TabControl, there's no way
the app will see the events or postback data of the children unless
it's raised explicitly.

The content of the Tabs needs to be rendered by the TabControl, but it
can't be a part of the TabControl in terms of the control hierarchy.

So now I have a different problem, and maybe it's more easily solved.
On the one hand, I need to be able to access the contents of the Tabs
during Render so that I can render them, but on the other hand, I can't
have them be children of the Tabs.  Is there any way in the world that
I can accomplish that?

Thanks again,
Lisa



Brock Allen wrote:
> Sorry Lisa, I can't quite tell exactly what the problem is from this.
What
> I'd suggest is to build a small sample outside of your current
project that
> works the way you want it and then try to compare the differences.
Here are
> some samples that might get you started:
>
>
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcustomizingcontrolswithtemplates.asp?frame=true
>
>
http://winfx.msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_aspnetctrlauth/html/986f63b8-6b50-42b9-a62d-a2f13cafa88b.asp
>
> And more of the same here:
>
>
http://samples.gotdotnet.com/quickstart/aspplus/default.aspx?url=%2fquickstart%2faspplus%2fdoc%2fwebctrlauthoring.aspx
Show quoteHide quote
>
> -Brock
> DevelopMentor
> http://staff.develop.com/ballen
>
>
>
> > Brock Allen wrote:
> >
> >>> It's a bit long, but here it is.  I appreciate the help.
> >>>
> >> Ugg, I should have said "post the smallest amount of code that
> >>
> > reproduces
> >
> >> the problem".
> >>
> > I'm sorry.  I honestly wasn't sure what part that would be.
Although
> > I suppose I could have left out the designer. <sheepish grin>
> >
> >> Ok, so I skimmed the code and, IIRC, your problem is that declared
> >>
> > controls
> >
> >> aren't getting server side events called? I think past of the
problem
> >>
> > is
> >
> >> that the controls aren't part of the control hierarchy... meaning,
I
> >>
> > don't
> >
> >> see where the controls that are in the TabdCollection are ever
added
> >>
> > into
> >
> >> the control tree. If they're not in the control tree then they
miss
> >>
> > out on
> >
> >> a lot of the server side processing. Again, that was a huge code
> >>
> > dump, so
> >
> >> my quick glance at it might be off.
> >>
> > I apologize for the size, Brock.  Here is what it looks like in the
> > HTML at design time:
> >
> > <fw:TabControl id="TabControl1" runat="server">
> > <fw:Tab ID="TabControl1__ctl0" Text="Tab 1">&nbsp;</fw:Tab>
> > <fw:Tab ID="TabControl1__ctl1" Text="Tab 2">
> > <asp:Button id="Button1" runat="server" Text="Button" />
> > </fw:Tab>
> > </fw:TabControl>
> > When I dragged it onto the form and added two tabs, it looked just
> > like this, except that there was a non-breaking space where the
button
> > is.
> >
> > When I'm in run time, I see this in the Command Window:
> >
> > ?me.Tabs(1).Controls(1).ID
> > "Button1"
> > "Me", in this case, is the TabControl.  Maybe I don't understand
the
> > control hierarchy, but doesn't the fact that I can reference the
> > button at run time this way mean that the button is part of the
> > control hierarchy?
> >
> > In terms of how the Tabs themselves are added, the TabCollection
has
> > this:
> >
> > Public Sub Add(ByVal myTab As Tab)
> > MyBase.List.Add(myTab)
> > End Sub 'Add
> > And the TabControl has this in global declarations:
> >
> > Private _tabs As New TabCollection
> >
> > And it has this property:
> >
> > Public ReadOnly Property Tabs() As TabCollection
> > Get
> > Return _tabs
> > End Get
> > End Property
> > Assuming that that's all the relevant code (which I'm unsure of),
do
> > you see what could be causing my problem?  Since the Button is
already
> > visible at run time as a child of the second tab, I don't know what
I
> > could add the Button to without it creating a second Button.
> >
> > The Microsoft Multipage control works the same way.  I can add a
> > Button (let's say) between the start and end tags of a PageView
> > control (analogous to my Tab control), and somehow, the Button's
> > events are called properly.  The biggest difference I can see is
that
> > their PageViewCollection is derived from ControlCollection, while
my
> > TabCollection is derived from CollectionBase.  But I can't see how
> > that could be causing the problems.
> >
> > I really appreciate the help, Brock.
> >
> > Lisa
> >