XTabControl服务器控件

分享于 

16分钟阅读

Web开发

  繁體

Sample Image - xtabcontrol.gif

介绍

当我在做普通旧的ASP ( 啊,那些天) 时,我弄骗了创建一个像我曾经用过的to类型的DHTML选项卡。 当你单击它们时,重新排列自己的多行选项卡,以及这种类型的选项卡。

在 ASP.NET 中编程时,我发现事情并不那么灵活。 我希望选项卡控件方便,但是我找到了孤立的TabStripMultiPage 控件,它们必须安装在服务器上,用户可以使用这些控件进行操作。 htc文件,必须单独处理,而不是很友好。

我喜欢友好的控制。

我在互联网上查看了一遍,找不到什么类似我想要的东西,所以我决定自己做。

XTable 控件一样,这个可能不是 跨浏览器 兼容的。 但是,我还没有在它的他浏览器上尝试过,所以可能会令我感到惊讶。

false 启动

XTabControl 是我创建的第三个选项卡控件。 前两个是可怕的,不规则的事情。 我所学到的有趣的事情之一是集合。

微软经常推荐人们从 CollectionBase 中获取 inherit的集合。 如果希望集合的元素能够包含其他控件,请不要这样做。 尽管这些控件都显示在控件层次中,但它们在控件层次结构中却没有出现,它们在 postback 中被忽略。 我尝试使用隐喻线和鸭子磁带强制控制保持他们的状态和数据来通过,而且我很成功。 但是我不能,因为爱或者钱,让他们在服务器上引发他们的事件。

最后,我决定替换我已经知道的代码。

这 还 差不多

我曾经在 making control中使用 roeder Lutz反射器来获取标准的微软 Table 服务器控件的代码,当我做了 XTable 控件时。 我把这个包括在源代码中作为 OpenTable

我认为,放入 TableCell s的控件没有我运行的问题,所以我可以用这个代码作为选项卡控件。 当然,Table 控件有三级层次结构(。Table -> TableRow -> TableCell ),我只需要两级( XTabControlXTab ),所以我基于 TableRow 类对控件进行了控制。

这个架构与我之前尝试的完全不同。 web上定义为类的TableCellCollection 类被定义为一个类,但 TableRow 类也包含了一个嵌套的CellControlCollection,它也成为我的XTable 类。 这些术语有点混乱,因为它听起来像是 XTabControl的集合,但是我认为它嵌套在 XTabControl 中了。

XTabCollection 并没有从任何方面进行 inherit,而是实现 IListICollectionIEnumerableXTabControl.XTabControlCollection 继承自 ControlCollection,它确保每个 XTab 和每个 XTab的子控件都是控件层次结构的一部分。

设计和属性

真正 irked TabControl的一个事情是无法更改选项卡的background 颜色。 我决定不要在我的控制中犯同样的错误。

一些特性很明显,比如 TabsSelectedIndexSelectedTab。 其他的是 LESS,所以我决定的非明显特性是:

  • XTabControl.TabsPerRow
  • XTabControl.TabHeight
  • XTabControl.TabFontFamily
  • XTabControl.TabFontSize
  • XTab.BackColor
  • XTab.ForeColor
  • XTab.InnerWidth
  • XTab.InnerHeight

TabsPerRow 属性是我从 VB6 TabControl 中记住的东西。 TabHeight 将确定所有 XTab的高度。 这必须是全局的,因为如果不可以能的话,XTab的高度将是不可以不必要的。 我可能会把 TabFontFamilyTabFontSize 属性放在个人 XTab 中,但是我觉得这会很丑。

除了允许用户选择每个标签的颜色,ForeColorBackColor,我决定不应该限制 XTab 上可用的空间,而不受 XTabControl的维度限制。 通过在每个 XTab 上创建 InnerWidthInnerHeight 属性,可以使用比可用的更多或者 LESS 空间。 演示项目展示了这一点,本文开头的GIF也是如此。

当然,最复杂的问题是将选项卡调整成正确的默认位置,并允许他们在用户单击时移动。 如果要将标签页中的某一行按下选项卡,则必须将整个行向下放置,然后才能立即显示该选项卡页,而我需要模拟这里行为。

对于标签位置和大小,在后面的行中,我编写了一个计算每个选项卡所需宽度的方法,这是在部分行中,如果有一个选项卡的话,那就需要。

例如如果 TabsPerRow 设置为 3,并且控件中有 5 XTab,那么将有一整行 3 XTab 和一部分 XTab。 这个函数很重,然后我创建了一个交错的Pair array 来保存对正确 XTab的引用。 对于每个 PairFirst 将保持 XTab.Index 值,Second 将保持该 XTab的宽度。 在渲染过程中,我只读了这个 array。

'''<summary>''' A series of calculations used to determine the width and position of''' each Tab in the TabControl. Unless you enjoy math, don't worry about it.'''</summary>'''<returns>A jagged array that represents the tabs in their rows.</returns>PrivateSub GetTabRows(ByRef _rowArray As Pair()(), _
 ByRef _selectedRow AsInteger, _
 ByRef _tabPageHeight As Unit)
 Dim _allTabCount AsInteger = Tabs.Count
 Dim _showTabCount AsInteger = _allTabCount
 Dim _fullRows AsIntegerDim _partRowTabWidth As Unit
 Dim _widthValue AsInteger = CInt(Width.Value)
 'if this is runtime, don't show Tabs that aren't visibleIfNot HttpContext.Current IsNothingThenDim _visibleTabCount AsInteger = 0ForEach _tab As XTab In Tabs
 If _tab.Visible Then _visibleTabCount += 1EndIfNext _tab
 _showTabCount = _visibleTabCount
 EndIf'get the number of tabs in full rows and their widthsDim _tabsInFullRow AsInteger = TabsPerRow
 Dim _fullRowTabWidth As Unit = _
 Unit.Pixel(CInt(Math.Floor(_widthValue/_
 CDbl(_tabsInFullRow))))
 'get the number of rows, totalDim _tabRows AsInteger = _
 CInt(Math.Ceiling(CDbl(_showTabCount)/_
 CDbl(_tabsInFullRow)))
 'get the number of tabs in a partial row (a row with 'fewer than TabsPerRow tabs)If _tabRows * _tabsInFullRow = _showTabCount Then _fullRows = _tabRows
 Else _fullRows = _tabRows - 1EndIfDim _tabsInPartRow AsInteger = _showTabCount - _
 (_fullRows * _tabsInFullRow)
 'get the widths of tabs in a partial rowIf _tabsInPartRow> 0Then _partRowTabWidth = _
 Unit.Pixel(CInt(Math.Floor(_widthValue/_
 CDbl(_tabsInPartRow))))
 Else _partRowTabWidth = _fullRowTabWidth
 EndIf'but just in case they don't divide roundly, we need the remaindersDim _fullRowRemainder AsInteger = _
 CInt(_widthValue - _fullRowTabWidth.Value * _tabsInFullRow)
 Dim _partRowRemainder AsInteger = _
 CInt(_widthValue - _partRowTabWidth.Value * _tabsInPartRow)
 'figure out the height of the masterPage _tabPageHeight = Unit.Pixel(CInt(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.SecondReDim _rowArray(_tabRows - 1)
 For i AsInteger = 0To _tabRows - 1If _fullRows <_tabRows And i = 0ThenReDim _rowArray(i)(_tabsInPartRow - 1)
 For j AsInteger = 0To _tabsInPartRow - 1If j = 0Then'add in the remainder _rowArray(i)(j) = New Pair(-5, _
 Unit.Pixel(CInt(_partRowTabWidth.Value _
 + _partRowRemainder)))
 Else _rowArray(i)(j) = New Pair(-4, _partRowTabWidth)
 EndIfNext j
 ElseReDim _rowArray(i)(_tabsInFullRow - 1)
 For j AsInteger = 0To _tabsInFullRow - 1If j = 0Then'add in the remainder _rowArray(i)(j) = New Pair(-3, _
 Unit.Pixel(CInt(_fullRowTabWidth.Value + _
 _fullRowRemainder)))
 Else _rowArray(i)(j) = New Pair(-2, _fullRowTabWidth)
 EndIfNext j
 EndIfNext i
 'now let's fill that array with tab indices (that goes into Pair.First)Dim _tabCollectionCounter AsInteger = 0Dim _tabCounter AsInteger = 0Dim _rowCounter AsInteger = _tabRows - 1DoWhile _tabCollectionCounter <_allTabCount
 If HttpContext.Current IsNothingOr _
 Tabs(_tabCollectionCounter).Visible Then _rowArray(_rowCounter)(_tabCounter).First = _tabCollectionCounter
 If _tabCollectionCounter = SelectedIndex Then _selectedRow = _rowCounter
 EndIf _tabCounter += 1If _tabCounter = _tabsInFullRow Then _rowCounter -= 1 _tabCounter = 0EndIfEndIf _tabCollectionCounter += 1LoopEndSub'GetTabRows

当用户选择一个 XTab 时,我希望开发人员能够选择 XTabControl 是否为 postback。 这意味着有一个客户端的方式从一个标签移动到另一个标签。 在项目中嵌入了一个VBScript文件,它在加载时被写入页面。

function XTabControl_SelectTab(obj)
 set objRow = obj.parentElement.parentElement
 set objTable = objRow.parentElement
 set objMasterPage = objTable.rows(objTable.rows.length-1).cells(0).children(0)
 TabControlName = left(obj.name, instrrev(obj.name, "tab_") - 2)
 helperControlName = "__" & TabControlName & "_State__"set objHelper = document.getElementById(helperControlName)
 tabIndex = mid(obj.name, instrrev(obj.name, "tab_") + 4)
 'select the panel selectedPanelName = TabControlName + "_panel_" + tabIndex
 foreach i in objMasterPage.children
 if i.name = selectedPanelName then i.style.display = "inline"else i.style.display = "none"endifnext'//set the masterPanel color objMasterPage.style.backgroundColor = obj.style.backgroundColor
 'format the tabs 
 'move the row that contains the selected tab down to just 上面 the masterPanel objTable.moveRow objRow.rowIndex, objTable.rows.length-2
 'make the selected tab"tabon" and the others in its row"taboff"foreach myTab in objRow.cells(0).children
 if myTab.name = obj.name then myTab.style.borderBottom = "none"else myTab.style.borderBottom = "3px inset"endifnext 
 'make all the tabs that aren't in the selected tab's row"tabon"if objTable.rows.length> 2thenfor rowIdx = 0 to objTable.rows.length-3
 foreach myTab in objTable.rows(rowIdx).cells(0).children
 myTab.style.borderBottom = "none"nextnextendif 
 'set the helper field to the selectedindex objHelper.value = tabIndex
 set objRow = Nothingset objTable = Nothingset objMasterPage = Nothingset objHelper = Nothingendfunction

我决定在样式中使用 inline CSS,而不是样式表。 你不能控制页面上的它的他样式表,而不是风险冲突的名称,我使用 inline 方法。

在呈现过程中,我使用以下代码确定控件在单击时的行为方式:

'set the onclick depending on whether AutoPostBack is true or notIf _autoPostBack Then writer.AddAttribute(HtmlTextWriterAttribute.Onclick, _
 "jscript:" + ClientHelperID + ".value=" + _tabIdx.ToString _
 + ";" + Page.GetPostBackEventReference(Me, _tabIdx.ToString()), _
 False)
 writer.AddAttribute("onfocus", "jscript:" + ClientHelperID + _
 ".value=" + _tabIdx.ToString + ";" + _
 Page.GetPostBackEventReference(Me, _tabIdx.ToString()), False)Else writer.AddAttribute(HtmlTextWriterAttribute.Onclick, _
 "vbscript:XTabControl_SelectTab(me)", False)
 writer.AddAttribute("onfocus", _
 "vbscript:XTabControl_SelectTab(me)", False)EndIf

这里代码段中引用的HelperID 是页上的隐藏控件,它保存值的XTabControlSelectedIndex。 i XTable 控件中的cribbed brazenly,它是 TabStrip 控件中的,一般情况下,它是 SelectedIndex 属性周围的很多力学。

已知问题

关于 VS.NET的一个比较烦人的事情是包含子控件可以被忽略。 例如如果将 TextBox 添加到 TableCell,则不能再在设计视图中编辑 TextBox 属性。 可以看到 TextBox,但是单击它只选择它所在的Table。 如果在HTML视图中向 TableCell 添加 TextBox ( 或者任何其他的控制),则 VS.NET 不会自动添加 protected WithEvents 在后面代码的"web窗体设计器生成的代码"区域中的TextBox的声明。

当然,这种烦人的行为扩展到了 XTabControl。 当微软在他们的终端上修复这个问题时,我希望它能自动解决 XTabControl的问题。 同时,你可能不习惯。

我认为尝试 ReadWriteControlDesigner 类,但是决定我没有足够的感觉。 这个控件的构建方式使用它可以能允许用户掌握控件的组成,因这里我很警告。 但如果有人想要一个旋转,我很高兴听到结果的结果。 如果它们很好,我可能会对 XTable 做同样的事情。


Server  控制  
相关文章