我希望那里的人能够阐明创建服务器控件,控制基类和这些类的用法的主题。
这是我想要实现的一个例子。我想创建一个自定义面板控件,可以在ASPX标记中实例化,如下所示:
<acme:Panel ID="MyPanel" runtat="server" Scrolling="true">
<Header>My Panel Header</Header>
<Toolbars>
<acme:Toolbar ID="Toolbar1" runat="server"/>
<acme:Toolbar ID="Toolbar2" runat="server"/>
</Toolbars>
<Contents>
<%-- Some Content for the Contents section --%>
</Contents>
<Footer>
<%-- Some Content for the Footer section --%>
</Footer>
</acme:Panel>
它应该呈现以下HTML:
<div id="MyPanel" class="panel scroll-contents">
<div class="panel-header">
<div class="panel-header-l"></div>
<div class="panel-header-c">
<div class="panel-header-wrapper">My Panel Header</div>
</div>
<div class="panel-header-r"></div>
</div>
<div class="panel-toolbars">
// HTML of Toolbar control
</div>
<div class="panel-body">
<div class="panel-body-t">
<div class="panel-body-tl"></div>
<div class="panel-body-tc"></div>
<div class="panel-body-tr"></div>
</div>
<div class="panel-body-m">
<div class="panel-body-ml"></div>
<div class="panel-body-mc">
<div class="panel-body-wrapper">
// Contents
</div>
</div>
<div class="panel-body-mr"></div>
</div>
<div class="panel-body-b">
<div class="panel-body-bl"></div>
<div class="panel-body-bc"></div>
<div class="panel-body-br"></div>
</div>
</div>
<div class="panel-footer">
<div class="panel-footer-l"></div>
<div class="panel-footer-c">
<div class="panel-footer-wrapper">
// Footer contents
</div>
</div>
<div class="panel-footer-r"></div>
</div>
</div>
开发人员应该能够省略除“内容”部分之外的任何部分。不应呈现省略的部分的HTML。此外,用户应该能够在页面后面的代码中实例化/添加Panel控件,并向Panel控件的各个部分添加其他控件,如下所示:
ACME.Panel MyPanel = new ACME.Panel();
MyPlaceHolder.Controls.Add(MyPanel);
MyPanel.Header = "My Panel Header";
MyPanel.Toolbars.Controls.Add(new ACME.Toolbar());
MyPanel.Footer.Controls.Add(new Literal());
MyPanel.Contents.Controls.Add(new GridView());
我已阅读以下文章:通信部门对话:编写自定义ASP.NET服务器控件(哪个基类?)
由于我不希望开发人员改变我的控件的样式,System.Web.UI.Control基类应该足够了,但我还需要应用INamingContainer接口。因此我的控制应该是这样的:
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Security.Permissions;
namespace ACME
{
[ToolboxData("<{0}:Panel runat=server></{0}:Panel >")]
[ParseChildren(true)]
public class Panel : Control, INamingContainer
{
private string _header;
private ITemplate _toolbars;
private ITemplate _contents;
private ITemplate _footerContents;
public DialogBox()
{
}
[Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty)]
public virtual ITemplate Toolbars
{
get { return _toolbars; }
set { _toolbars = value; }
}
[Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty)]
public virtual ITemplate Contents
{
get { return _contents; }
set { _contents= value; }
}
[Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty)]
public virtual ITemplate Footer
{
get { return _footerContents; }
set { _footerContents = value; }
}
}
}
我已阅读了许多教程,但它们要么不涵盖我的预期实现,要么解释为什么采取某种方法。他们也让我很困惑,以至于我使用JavaScript来动态呈现必要的HTML。如果有任何控制大师在那里,你能解释一下你将如何处理这项任务吗?
答案 0 :(得分:2)
这将是一个漫长的过程,需要遵循许多代码:
因为你想要渲染很多常见元素,所以我开始使用BaseControl定义一些常用方法来生成你想要的所有div。这继承自System.Web.UI.Control - 作为文档状态:
这是您在开发自定义ASP.NET服务器控件时派生的主类。 控件没有任何用户界面(UI)特定功能。如果您正在创作没有UI的控件,或者组合了呈现自己的UI的其他控件,则可以从控件派生。
所以基本控件看起来像这样:
/// <summary>
/// Provides some common methods.
/// </summary>
public class BaseControl: Control
{
protected Control[] TempControls;
/// <summary>
/// Clears the child controls explicitly, and stores them locally.
/// </summary>
protected void ClearControls()
{
if (HasControls())
{
TempControls = new Control[Controls.Count];
Controls.CopyTo(TempControls, 0);
}
Controls.Clear();
}
/// <summary>
/// Creates a new panel (HTML div) with the requested CSS
/// and containing any controls passed in.
/// </summary>
/// <param name="cssClass">The CSS class to be applied</param>
/// <param name="controls">Any controls that should be added to the panel</param>
protected Panel NewPanel(string cssClass, params Control[] controls)
{
// Create a new Panel, assign the CSS class.
var panel = new Panel { CssClass = cssClass };
// Loop through the controls adding them to the panel.
foreach (var control in controls)
{
panel.Controls.Add(control);
}
return panel;
}
/// <summary>
/// Creates a new row of panels (HTML div), based on the CSS class prefix.
/// The center panel holds the controls passed in.
/// </summary>
/// <param name="cssClassPrefix"></param>
/// <param name="controls"></param>
protected Panel NewRow(string cssClassPrefix, params Control[] controls)
{
// Expaned for clarity, but could all be passed in on one call.
var row = NewPanel(cssClassPrefix);
row.Controls.Add(NewPanel(cssClassPrefix + "-l"));
row.Controls.Add(NewPanel(cssClassPrefix + "-c", controls));
row.Controls.Add(NewPanel(cssClassPrefix + "-r"));
return row;
}
}
然后你需要创建一些控件来处理你的每个模板,我在这里创建了两个。
首先,一个生成一行的控件 - 由页眉和页脚使用:
public class AcmeSimple : BaseControl, INamingContainer
{
private string m_CssPrefix;
public AcmeSimple(string cssPrefix)
{
m_CssPrefix = cssPrefix;
}
protected override void CreateChildControls()
{
ClearControls();
Panel wrapper = NewPanel(m_CssPrefix + "-wrapper", TempControls);
Panel simple = NewRow(m_CssPrefix, wrapper);
Controls.Add(simple);
base.CreateChildControls();
}
}
它创建一个新面板来保存控件,然后创建一个新的div行来保存包装器。
然后是一个稍微复杂的内容控件,其工作原理与标题相同:
public class AcmeContents: BaseControl, INamingContainer
{
protected override void CreateChildControls()
{
ClearControls();
Panel wrapper = NewPanel("panel-body-wrapper", TempControls);
Panel contents = NewPanel("panel-body");
contents.Controls.Add(NewRow("panel-body-t"));
contents.Controls.Add(NewRow("panel-body-m", wrapper));
contents.Controls.Add(NewRow("panel-body-b"));
Controls.Add(contents);
base.CreateChildControls();
}
}
所以这个只创建了三行,其中一行包含控件。
最后,您放置在页面上的实际控件:
[ParseChildren(true)]
[ToolboxData("<{0}:AcmeControl runat=server></{0}:AcmeControl>")]
public class AcmeControl: BaseControl, INamingContainer
{
public bool Scrolling { get; set; }
[TemplateContainer(typeof(AcmeSimple))]
public ITemplate Header { get; set; }
[TemplateContainer(typeof(AcmeContents))]
public ITemplate Contents { get; set; }
[TemplateContainer(typeof(AcmeSimple))]
public ITemplate Footer { get; set; }
protected override void CreateChildControls()
{
Controls.Clear();
string cssClass = "panel";
if (Scrolling)
{
cssClass += " scrollContents";
}
Panel panel = NewPanel(cssClass);
panel.ID = ID;
Controls.Add(panel);
if (Header != null)
{
var header = new AcmeHeader("panel-header");
Header.InstantiateIn(header);
panel.Controls.Add(header);
}
if (Contents != null)
{
var contents = new AcmeContents();
Contents.InstantiateIn(contents);
panel.Controls.Add(contents);
}
else
{
// Possibly a little harsh, as it's a runtime exception.
throw new ArgumentNullException("Contents", "You must supply a contents template.");
}
if (Footer != null)
{
var footer = new AcmeSimple("panel-footer");
Footer.InstantiateIn(footer);
panel.Controls.Add(footer);
}
}
}
因此,我们将支持的模板定义为控件的属性,以及您想要的Scrollable属性。然后,在CreateChildControls中,我们开始使用我们在开始时创建的控件和BaseControl中的方法构建控件的主体。
这就是这样的页面:
<cc1:AcmeControl ID="AcmeControl1" runat="server">
<Header>
<b>Here's a header</b>
</Header>
<Contents>
<i>Here's some controls in the content.</i>
</Contents>
</cc1:AcmeControl>
并且这样渲染出来:
<div id="AcmeControl1_AcmeControl1" class="panel">
<div class="panel-header">
<div class="panel-header-l">
</div>
<div class="panel-header-c">
<div class="panel-header-wrapper">
<b>Here's a header</b>
</div>
</div>
<div class="panel-header-r">
</div>
</div>
<div class="panel-body">
<div class="panel-body-t">
<div class="panel-body-t-l">
</div>
<div class="panel-body-t-c">
</div>
<div class="panel-body-t-r">
</div>
</div>
<div class="panel-body-m">
<div class="panel-body-m-l">
</div>
<div class="panel-body-m-c">
<div class="panel-body-wrapper">
<i>Here's some controls in the content.</i>
</div>
</div>
<div class="panel-body-m-r">
</div>
</div>
<div class="panel-body-b">
<div class="panel-body-b-l">
</div>
<div class="panel-body-b-c">
</div>
<div class="panel-body-b-r">
</div>
</div>
</div>
</div>
所以唯一的区别是内容样式是t-l而不是tl。
然而(这对你来说可能是一个大问题),模板控件实际上并不是设计用于从代码隐藏中填充 - 你会注意到尝试编写:
AcmeControl1.Footer.Controls.Add([...]);
不会编译。
你可以做的是打电话:
AcmeControl1.Footer = Page.LoadTemplate([...])
并将路径传递给 ascx 文件。
可以找到有关创建模板化控件的进一步阅读:
我说会很久。