Collection disappearing at run time

L

lisa

I really, really wanted to finish this one without asking anyone for
help. But I've been beating my head against this thing, and I'm
getting nowhere.

I wrote a tab control. I cribbed some ideas from the BlueValley one,
but mostly after I'd finished the main stuff.

It works just great in design time (the opposite of the usual problem).
Except that if I try to add any content to any of the tabs in the HTML
view, when I go back to design view, the control won't render anymore.
The same thing happens if I try adding content to a tab
programmatically. At least I think. The problem with knowing for sure
is that if I try to view a page that has a tab control on it, whether I
add content or not, the same thing happens, and I get an error.

When I trace the error, I find that either running the page or adding
content in HTML view or both causes the tabs collection to evaporate
into Nothing.

I've tried debugging in every way I can, but I can't figure out what's
making the collection not persist. I'm posting the code here. Feel
free to take it and use it for yourself, if it becomes fixable. But if
anyone can tell me what I'm doing wrong, I'd very much appreciate it.

Um... it's IE only. Again. Sorry.

TIA,
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

<ToolboxData("<{0}:TabStrip runat=server></{0}:TabStrip>"),
Designer(GetType(TabStripDesigner)), PersistChildren(False),
ParseChildren(True, "Tabs"), DefaultProperty("Tabs"),
DefaultEvent("SelectedIndexChanged")> _
Public Class TabStrip
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 _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 _visibleTabs As Integer
Private _rowArray As Pair()()
Private _selectedRow As Integer
Private _tabIdx As Integer
Private _firstVisibleTabIdx As Integer = -1

<Category("Behavior"), Description("The collection of tabs."),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
NotifyParentProperty(True),
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),
NotifyParentProperty(True), PersistenceMode(PersistenceMode.Attribute)>
_
Public Property TabsPerRow() As Integer
Get
Return _tabsPerRow
End Get
Set(ByVal Value As Integer)
_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

<DefaultValue(True)> _
Public Property AutoPostBack() As Boolean
Get
Return _autoPostBack
End Get
Set(ByVal Value As Boolean)
_autoPostBack = Value
End Set
End Property

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("TabStrip_vbs") Then
Dim reader As New
System.IO.StreamReader(Me.GetType().Assembly.GetManifestResourceStream(Me.GetType(),
"TabStrip.vbs"))
Dim script As String = "<script language='vbscript'
type='text/vbscript' >" _
+ ControlChars.CrLf _
+ "<!--" _
+ ControlChars.CrLf _
+ reader.ReadToEnd() _
+ ControlChars.CrLf _
+ "//-->" _
+ ControlChars.CrLf _
+ "</script>"
Page.RegisterClientScriptBlock("TabStrip_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 > _tabCount - 1
Then
_selectedIndex = 0
End If
End If

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

End Function 'GetSelectedIndexForRender

Private Sub GetTabFormatValuesForRender()

'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(_visibleTabs) /
CDbl(_tabsInFullRow)), Integer)

'get the number of tabs in a partial row (a row with fewer than
TabsPerRow tabs)
If _tabRows * _tabsInFullRow = _visibleTabs Then
_fullRows = _tabRows
Else
_fullRows = _tabRows - 1
End If
_tabsInPartRow = _visibleTabs - (_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
If Tabs(_tabCollectionCounter).Visible Then
_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
End If
_tabCollectionCounter = _tabCollectionCounter + 1
Loop

End Sub 'GetTabFormatValuesForRender

Protected Overrides Sub Render(ByVal writer As
System.Web.UI.HtmlTextWriter)

'get the number of visible tabs
_visibleTabs = 0
For Each myTab As Tab In Tabs
If myTab.Visible Then
_visibleTabs = _visibleTabs + 1
End If
Next

'if there are no visible tabs, don't render the control
If _visibleTabs = 0 Then
Exit Sub
End If

'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()

'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("font-family", "system")
writer.AddStyleAttribute("font-size", "8pt")
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,
Tabs(_tabIdx).ForeColor.Name)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
Tabs(_tabIdx).BackColor.Name)
'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,
"TabStrip_SelectTab(this)", False)
End If
writer.AddAttribute(HtmlTextWriterAttribute.Name,
"tab_" & _tabIdx.ToString, False)
writer.AddAttribute(HtmlTextWriterAttribute.Id,
"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", "system")
writer.AddStyleAttribute("font-size", "8pt")
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,
Tabs(_tabIdx).ForeColor.Name)

writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,
Tabs(_tabIdx).BackColor.Name)
'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,
"TabStrip_SelectTab(this)", False)
End If
writer.AddAttribute(HtmlTextWriterAttribute.Name, "tab_" &
_tabIdx.ToString, False)
writer.AddAttribute(HtmlTextWriterAttribute.Id, "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
myControl.RenderControl(writer)
Next

writer.RenderEndTag() 'Div
End If
Next

writer.RenderEndTag() 'Div
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

End Class 'TabStrip

<ToolboxItem(False)> _
Public Class Tab
Inherits Control

Private _text As String
Private _innerHeight As Unit
Private _innerWidth As Unit
Private _enabled As Boolean
Private _visible As Boolean
Private _foreColor As System.Drawing.Color
Private _backColor As System.Drawing.Color

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

<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(True), Category("Behavior"), Description("Whether tab
can be selected and contents accessed in runtime or not.")> _
Public Property Enabled() As Boolean
Get
Return _enabled
End Get
Set(ByVal Value As Boolean)
_enabled = Value
End Set
End Property 'Enabled

<Browsable(True), Category("Behavior"), Description("Whether tab is
visible at runtime or not.")> _
Public Overrides Property Visible() As Boolean
Get
Return _visible
End Get
Set(ByVal Value As Boolean)
_visible = Value
End Set
End Property 'Visible

<Browsable(True), Category("Appearance"), Description("The color of
the text appearing on the tab.")> _
Public Property ForeColor() As System.Drawing.Color
Get
Return _foreColor
End Get
Set(ByVal Value As System.Drawing.Color)
_foreColor = Value
End Set
End Property 'ForeColor

<Browsable(True), Category("Appearance"), Description("The
background of the tab.")> _
Public Property BackColor() As System.Drawing.Color
Get
Return _backColor
End Get
Set(ByVal Value As System.Drawing.Color)
_backColor = Value
End Set
End Property 'BackColor

End Class 'Tab

Public Class TabCollection
Inherits CollectionBase

Default Public ReadOnly Property Item(ByVal index As Integer) As
Tab
Get
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 TabStripDesigner
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 tabStrip As tabStrip = CType(component, tabStrip)
DesignTimeSelectedIndex = tabStrip.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'>"
strHtml += "<tr><td><b>TabStrip</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 tabStrip As tabStrip = CType(Me.Component, tabStrip)
If Not (tabStrip Is Nothing) And tabStrip.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 = tabStrip.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 = tabStrip.AutoPostBack
Dim _tabHeight As Unit = tabStrip.TabHeight
Dim _width As Unit = tabStrip.Width
Dim _height As Unit = tabStrip.Height
Dim _tabCount As Integer = tabStrip.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
If tabStrip.Tabs(_tabCollectionCounter).Visible Then
_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
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",
"system")
writer.AddStyleAttribute("font-size", "8pt")
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,
tabStrip.Tabs(_tabIdx).ForeColor.Name)

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

writer.RenderBeginTag(HtmlTextWriterTag.Div)
writer.Write(tabStrip.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", "system")
writer.AddStyleAttribute("font-size", "8pt")
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,
tabStrip.Tabs(_tabIdx).ForeColor.Name)

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

writer.RenderBeginTag(HtmlTextWriterTag.Div)
writer.Write(tabStrip.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,
tabStrip.Tabs(tabStrip.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 tabStrip.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 =
tabStrip.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 tabStrip As tabStrip = CType(Me.Component, tabStrip)
verbShowNextTab.Visible = False
verbRemoveTab.Visible = False
verbShowNextTab.Visible = tabStrip.Tabs.Count > 1
verbRemoveTab.Visible = tabStrip.Tabs.Count > 0
End Sub 'ModifyMenu

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

Private Sub ShowNextTab(ByVal sender As Object, ByVal e As
EventArgs)
Dim tabStrip As tabStrip = CType(Me.Component, tabStrip)
Dim TabCount As Integer = tabStrip.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 tabStrip As tabStrip = CType(Me.Component, tabStrip)
If tabStrip.Tabs.Count > 0 Then
tabStrip.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 tabStrip.SelectedIndex >= tabStrip.Tabs.Count Then
tabStrip.SelectedIndex = tabStrip.Tabs.Count - 1
If tabStrip.SelectedIndex < 0 Then
tabStrip.SelectedIndex = 0
End If
End If
' Commit the Change
OnComponentChanged(Me, New
ComponentChangedEventArgs(tabStrip, Nothing, Nothing, Nothing))
' Show the closest, right-most sibling Tab in the Designer
DesignTimeSelectedIndex -= 1
If DesignTimeSelectedIndex + 1 >= tabStrip.Tabs.Count Then
DesignTimeSelectedIndex -= 1
End If
ShowNextTab(Me, EventArgs.Empty)
End If
End Sub 'RemoveTab

End Class 'TabControlDesigner
 
L

lisa

I figured out what to do to fix my problem, but I don't understand why
it was causing the problem to begin with. I changed the New method for
the Tab so that it doesn't take any arguments. And now it works. Go
figure. I guess I can live with the mystery, so long as the control is
working.

I made some other tweaks, because there were some minor bugs I hadn't
found. If anyone wants a copy of the control, let me know and I'll
either post it or put it up on my site so that you can download it.

Once I'm totally satisfied with it, I think I'm going to add remote
scripting capability, so that it can autopostback without the page
blinking. They deprecated the blink tag, but .NET is still giving us
eyeaches. That's got to go.

Lisa
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,832
Latest member
GlennSmall

Latest Threads

Top