创建基于web的选项卡控件

分享于 

15分钟阅读

Web开发

  繁體 雙語

介绍

我一直在想,为什么不提供一个简单的web选项卡控件,开发人员可以根据自己的需求和需求来扩展。 当然,有很多第三方控件提供了丰富和高级的选项卡控件,但是我找不到很多文章。

分割web窗体控件是必要的,当你有大量的控件 inside 表单时,它有助于组织表单。

贯穿本文,我将更加关注控件设计器,以便开发人员提供与 Windows 表单 Tab 控件类似的外观和感觉。 在运行时给出外观和感觉是一个明显的要求,不过这只是使用 HTML table 单元格颜色和一些图像,这将帮助提供 3D 外观。

背景

简单来说,选项卡控件包含两个内容: 选项卡标题和制表符正文。 在我的解决方案中,选项卡控件表示为 table,包含标题和制表符的单元格,类似于下表:

标题 1标题 2..标题n
选项卡 1的正文
选项卡 2的正文
..
制表符的正文

这就是如何在运行时表示tab控件,而且只有活动选项卡正文可以见,而所有它的他选项卡。

选项卡控件的设计和运行时视图

基本上,tab控件将由一个选项卡页 Collection,MyTabPageCollection,它是 MyTabPage 控件的Collection。 控件包含两个主要属性,即选项卡标题。Title 和选项卡体 TabBody 类型 ITemplateSystem.Web.UI.ITemplate 将允许设计和保存其他控件。

publicclass MyTabPage : System.Web.UI.WebControls.PlaceHolder
{
 privatestring _title = "";
 private ITemplate _tabBody;
 publicstring Title
 {
 get { return _title; }
 set { _title = value; }
 }
 [
 PersistenceMode(PersistenceMode.InnerProperty),
 DefaultValue(null),
 Browsable(false)
 ]
 publicvirtual ITemplate TabBody
 {
 get { return _tabBody; }
 set { _tabBody = value; }
 }
}

选项卡控件 MyTabControlCompositeControl的子类,包含所有选项卡,并包含一些属性,在设计时通过 CurrentDesignTab 属性保存活动选项卡,并在运行时通过 SelectedTab 属性保存活动选项卡。 这些属性用于保存活动选项卡的索引。 选项卡控件设计器 MyTabControlDesigner 将在选项卡模板之间 switch,并基于该索引激活它们。 多个选项卡保存在 MyTabPageCollection 类型的TabPages Collection 中;这里 Collection 将允许定义多个选项卡。 MyTabControl 实现了两种主要方法: OnPreRenderCreateChildControls

protectedoverridevoid OnPreRender(EventArgs e)
{
 base.OnPreRender(e);
 if (DesignMode)
 {
 _tabPages[_currentDesignTab].TabBody.InstantiateIn(this);
 }
}protectedoverridevoid CreateChildControls()
{
 // Always start with a clean form Controls.Clear();
 // Create a table using the control's declarative properties Table tabControlTable = new Table();
 tabControlTable.CellSpacing = 1;
 tabControlTable.CellPadding = 0;
 tabControlTable.BorderStyle = BorderStyle;
 tabControlTable.Width = this.Width;
 tabControlTable.Height = this.Height;
 tabControlTable.BackColor = ColorTranslator.FromHtml("inactiveborder");
 //keep the selected tab index in a an attribute tabControlTable.Attributes.Add("ActiveTabIdx", _selectedTab.ToString());
 BuildTitles(tabControlTable);
 BuildContentRows(tabControlTable);
 // Add the finished tabControlTable to the Controls collection Controls.Add(tabControlTable);
}

design design OnPreRender 将根据模板实例化活动选项卡,选项卡控件中带有索引 _currentDesignTab,而 CreateChildControls 方法负责绘制控件内容,参见前面的布局。 此外,它将把 ActiveTabIdx 暴露给 table的属性,该属性代表标签容器。 选项卡标题将通过 BuildTitles 方法呈现,该方法循环访问 TabPages Collection,并将标题绘制为 table 单元格。 另外,它将创建对JavaScript函数 ShowTabOnClick 事件处理程序调用,该函数负责显示活动选项卡。

选项卡控件由保存标题和选项卡内容的table 表示。 在呈现标题之后,我们需要呈现标签内容。 嗯,BuildContentRows 方法负责呈现内容,它的中每个选项卡体由 table 中的单元格表示。 注意在表格第一行保存标题之后,选项卡内容行索引将从 1开始,因此活动选项卡位于行索引( ActiveTabIdx+1 )。和。

对于设计时视图,我们不需要实例化所有选项卡,但是只需要使用活动选项卡,就像你在方法 BuildContentRows 中所看到的。

privatevoid BuildContentRows(Table tabControlTable)
{
 // Create content row(s)if (DesignMode)
 {
 TableRow contentRow = new TableRow();
 TableCell contentCell = BuildContentCell(contentRow);
 _tabPages[_currentDesignTab].TabBody.InstantiateIn(contentCell);
 tabControlTable.Rows.Add(contentRow);
 }
 else {
 int counter = 0;
 foreach (MyTabPage tabPage in _tabPages)
 {
 TableRow contentRow = new TableRow();
 TableCell contentCell = BuildContentCell(contentRow);
 if (tabPage.TabBody!= null)
 {
 tabPage.TabBody.InstantiateIn(contentCell);
 }
 //only the selected tab body should be visibleif (_selectedTab == counter)
 {
 contentRow.Style["display"] = "block";
 }
 else {
 contentRow.Style["display"] = "none";
 }
 contentRow.Cells.Add(contentCell);
 tabControlTable.Rows.Add(contentRow);
 counter++;
 }
 }
}

对于运行时视图,过程将迭代抛出所有选项卡,并在内容行中实例化选项卡体。

在客户端的选项卡之间切换

当用户单击选项卡标题时,客户端事件处理程序 ShowTab 将在已知点击选项卡及其当前索引的情况下 switch,内联注释提供该过程的工作方式。

<script type="text/javascript" language="javascript">function ShowTab(tabTitleCell, idx)
{ 
 //get the table which holds the tabsvar tabsTable = tabTitleCell.parentElement.parentElement.parentElement;
 //what is the active tab indexvar activeTabIdx = Number(tabsTable.getAttribute("ActiveTabIdx"));
 //give the inactive appearance to the previous active tab tabsTable.rows[0].cells[activeTabIdx].style["backgroundColor"] = "inactiveborder";
 tabsTable.rows[0].cells[idx].style["backgroundColor"] = "darkgray";
 //since the tabs body contained in rows with //index as the same of the tab title link plus 1,//then we can hide the row that holds the active tab. tabsTable.rows[activeTabIdx + 1].style.display = "none";
 //show the active tab body tabsTable.rows[idx + 1].style.display = "";
 //keep the new active tab in the attribute ActiveTabIdx tabsTable.setAttribute("ActiveTabIdx", idx);
}
<script>

管理选项卡控件模板并切换设计视图

选项卡控件设计器应该负责:

  • 声明设计区域。
  • 在标签之间切换。
  • 保存选项卡正文,并在设计器中查看它们。

CompositeControlDesigner 提供基本设计器,可以控制可以编辑区域并创建子控件。 MyTabControlDesigner 继承 CompositeControlDesigner,以支持选项卡控件设计视图。 设计器将 private 引用 tabControl 包含到选项卡控件中。 这里引用在 Initialize 方法中被初始化。 这里引用可以通过设计器逻辑来引用选项卡控件。

声明设计区域

在获得设计时HTML时,重写的方法 GetDesignTimeHtml 将为所有 header 单元添加设计区域;区域名与 HEADER_PREFIX 页索引一起。 这样,我们就可以从区域名称中提取标签索引。 然后,使用类似于 CONTENT_PREFIX 前缀和当前活动选项卡的索引的命名格式创建一个设计区域。

publicoverrideString GetDesignTimeHtml(DesignerRegionCollection regions)
{
 int i = 0;
 foreach (MyTabPage tabPage in tabControl.TabPages)
 {
 regions.Add(new DesignerRegion(this, 
 HEADER_PREFIX + i.ToString()));
 i++;
 }
 EditableDesignerRegion editableRegion =
 new EditableDesignerRegion(this,
 CONTENT_PREFIX + 
 tabControl.CurrentDesignTab, false);
 regions.Add(editableRegion);
 regions[tabControl.CurrentDesignTab].Highlight = true;
 returnbase.GetDesignTimeHtml();
}

将使用保存选项卡索引的设计器区域属性来标记所有选项卡标题单元格。 此外,内容单元格将在设计区域中标记。

protectedoverridevoid CreateChildControls()
{
 base.CreateChildControls();
 Table table = (Table) tabControl.Controls[0];
 if (table!= null)
 {
 for (int i = 0; i < tabControl.TabPages.Count; i++)
 {
 table.Rows[0].Cells[i].Attributes[
 DesignerRegion.DesignerRegionAttributeName] = 
 i.ToString();
 }
 table.Rows[1].Cells[0].Attributes[
 DesignerRegion.DesignerRegionAttributeName] = 
 (tabControl.TabPages.Count).ToString();
 }
}

在标签之间切换

当用户单击控件时,OnClick 将捕获事件。 如果单击区域是已知区域,代码将从单击标题的区域名称中提取选项卡索引,并将它的设置为选项卡控件属性( CurrentDesignTab )。 然后,UpdateDesignTimeHtml 将相应地更新设计视图。

protectedoverridevoid OnClick(DesignerRegionMouseEventArgs e)
{
 if (e.Region == null)
 return;
 if (e.Region.Name.IndexOf(HEADER_PREFIX)!= 0)
 return;
 if (e.Region.Name.Substring(HEADER_PREFIX.Length)!= 
 tabControl.CurrentDesignTab.ToString())
 {
 tabControl.CurrentDesignTab = 
 int.Parse(e.Region.Name.Substring(HEADER_PREFIX.Length));
 base.UpdateDesignTimeHtml();
 }
}

保留选项卡主体,并在设计器中查看它

在选项卡之间切换时,设计器需要获取表示活动选项卡的活动模板。 可以从活动区域名称获取选项卡索引,并通过从 tabControl.TabPages[tabIndex].TabBody 并通过 ControlPersister.PersistTemplate 方法返回 HTML。 请参见方法 GetEditableDesignerRegionContent

publicoverridestring GetEditableDesignerRegionContent(EditableDesignerRegion region)
{
 IDesignerHost host = (IDesignerHost)
 Component.Site.GetService(typeof(IDesignerHost));
 if (host!= null && tabControl.TabPages.Count >0)
 {
 ITemplate template = tabControl.TabPages[0].TabBody;
 if (region.Name.StartsWith(CONTENT_PREFIX))
 {
 int tabIndex = int.Parse(region.Name.Substring(
 CONTENT_PREFIX.Length));
 template = tabControl.TabPages[tabIndex].TabBody;
 }
 if (template!= null)
 return ControlPersister.PersistTemplate(template, host);
 }
 returnString.Empty;
}

对模板的任何更改都应反映到已经编辑选项卡的TabBody 中。 方法 ControlParser.ParseTemplate 将从设计内容中实例化模板,通过了解区域名称,我们可以获取选项卡索引,然后使用内容模板更新 TabBody。 请参见方法 SetEditableDesignerRegionContent

publicoverridevoid SetEditableDesignerRegionContent(
 EditableDesignerRegion region, string content)
{
 if (content == null)
 return;
 IDesignerHost host = (IDesignerHost)
 Component.Site.GetService(typeof(IDesignerHost));
 if (host!= null)
 {
 ITemplate template = ControlParser.ParseTemplate(host, content);
 if (template!= null)
 {
 if (region.Name.StartsWith(CONTENT_PREFIX))
 {
 int tabIndex = int.Parse(
 region.Name.Substring(CONTENT_PREFIX.Length));
 tabControl.TabPages[tabIndex].TabBody = template;
 }
 }
 }
}

最终

我没有对这个控件做很多编码,但我认为我提供了创建基于web的制表符控件的基础。 我希望这篇文章对你有帮助。 另外,为了提高渲染的逻辑和在标签之间切换的逻辑,我将保留它的创造力。 可以开发一个脚本对象来管理在客户端的tab控件,提供属性和选项卡 Collection,以自动切换。显示和启用选项卡。

如果你有它的他的想法,或者提高这里代码的方式,请随意使用这里代码并更新你喜欢的方式。 如果你能用你的增强功能更新我的话,我将非常感激。


WEB  BASE  控制  tab  基于web  
相关文章