【问题标题】:Custom Server Controls - Base Classes & Usage自定义服务器控件 - 基类和用法
【发布时间】:2009-05-07 20:09:31
【问题描述】:

我希望有人能对创建服务器控件、控件基类以及这些类的用法有所了解。

这是我想要实现的示例。我想创建一个可以在 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("&lt;{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。如果那里有任何控制大师,您能解释一下您是如何完成这项任务的吗?

【问题讨论】:

    标签: asp.net custom-server-controls


    【解决方案1】:

    这将是一个很长的代码,要遵循很多代码:

    因为您有很多想要渲染的通用元素,所以我从一个 BaseControl 开始,它定义了一些通用方法来生成您所追求的所有 div。这继承自 System.Web.UI.Control - 如文档所述:

    这是您在开发自定义 ASP.NET 服务器控件时派生的主要类。 Control 没有任何用户界面 (UI) 特定功能。如果您正在创作一个没有 UI 的控件,或者组合了呈现自己的 UI 的其他控件,请从 Control 派生。

    所以基本控件如下所示:

    /// <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 文件的路径。

    关于创建模板化控件的进一步阅读可以找到:

    我说会很长。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-08
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 2010-11-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多