2009-05-07 1 views
1

Ich hoffe, dass jemand da draußen etwas Licht in das Thema der Erstellung von Serversteuerelementen, der Steuerbasisklassen und der Verwendung dieser Klassen bringen kann.Benutzerdefinierte Serversteuerelemente - Basisklassen und Verwendung

Hier ist ein Beispiel, was ich erreichen möchte. Ich möchte ein eigenes Panel-Steuerelement erstellen, die man wie so in der ASPX-Markup instanziiert kann:

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

Es sollte die folgende HTML-Render:

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

Der Entwickler sollte eine der Lage sein, wegzulassen Abschnitte außer dem Abschnitt Inhalt. HTML der ausgelassenen Abschnitte sollte nicht gerendert werden. Außerdem sollte der Benutzer ein Panel-Steuerelement in den Code hinter der Seite der Lage sein, zu instanziiert/hinzufügen und zusätzliche Kontrollen an den verschiedenen Abschnitten des Panel-Steuerelement hinzufügen, etwa so:

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

ich den folgenden Artikel gelesen haben: Communications Sector Conversations: Erstellen benutzerdefinierter ASP.NET-Serversteuerelemente (Welche Basisklasse?)

Da ich nicht wirklich möchte, dass der Entwickler das Styling meines Steuerelements ändert, sollte die System.Web.UI.Control-Basisklasse ausreichend sein , aber ich muss auch die INamingContainer-Schnittstelle anwenden. So sollte meine Kontrolle wie so aussehen:

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; } 
     } 
    } 
} 

Ich habe viele Tutorials lesen, aber sie entweder nicht die beabsichtigte Umsetzung decken oder erklären, warum ein bestimmte Ansatz gemacht. Sie haben mich auch so sehr verwirrt, dass ich auf JavaScript zurückgegriffen habe, um das benötigte HTML dynamisch zu rendern. Wenn da draußen ein Kontrollguru ist, könntest du mir bitte erklären, wie du diese Aufgabe bewältigt hast?

Antwort

2

Das wird ein langer sein, eine Menge Code zu folgen:

Weil du eine Menge gemeinsamer Elemente, die Sie wollen gemacht haben aus, begann ich mit einem basecontrol, die einige gemeinsame Methoden definiert zu erzeugen alle Divs, nach denen du suchst. Dies erbt von System.Web.UI.Control - als Status der Dokumentation:

Dies ist die primäre Klasse, die Sie abgeleitet werden, wenn Sie benutzerdefinierte ASP.NET-Serversteuerelemente entwickeln. Die Steuerung verfügt über keine benutzeroberflächenspezifischen Funktionen. Wenn Sie ein Steuerelement erstellen, das keine Benutzeroberfläche hat, oder andere Steuerelemente kombinieren, die ihre eigene Benutzeroberfläche rendern, leiten Sie von Steuerelement.

So ist die Basis-Steuer sieht wie folgt aus:

/// <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; 
    } 
} 

Sie müssen dann einige Steuerelemente erstellen, die jede Ihrer Vorlagen behandeln wird, wie ich zwei hier erstellt haben.

Erstens, eine Steuerung, die eine einzelne Zeile generieren - verwendet sowohl die Kopf- und Fußzeile:

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(); 
    } 
} 

Es schafft eine neue Platte, die Kontrollen zu halten, und erstellt dann eine neue Reihe von divs halte den Umschlag.

Dann wird eine etwas komplexere Inhalte Kontrolle, die auf dem gleichen Prinzip wie der Kopf funktioniert:

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(); 
    } 
} 

Also das man nur drei Zeilen erstellt, in der Mitte von denen die Bedienelemente enthält.

Schließlich die tatsächliche Kontrolle, die Sie auf der Seite platzieren:

[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); 
     } 
    } 
} 

So definieren wir die Vorlagen, die wir als Eigenschaften der Steuerung unterstützen, zusammen mit der Scrollable Eigenschaft, die Sie wollten. In CreateChildControls beginnen wir dann, den Hauptteil des Steuerelements mit den Steuerelementen, die wir am Anfang erstellt haben, und den Methoden in BaseControl aufzubauen.

Dies geht auf die Seite wie folgt aus:

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

Und macht wie folgt aus:

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

So ist der einzige Unterschied ist, dass der Inhalt Arten tl sind eher als tl.

aber (und das ist ein großes Problem für Sie sein könnte), Template-Kontrollen sind nicht wirklich entworfen von Code-Behind gefüllt werden - Sie werden feststellen, dass zu schreiben versucht:

AcmeControl1.Footer.Controls.Add([...]); 

won‘ t kompilieren.

Was man jedoch tun, ist Aufruf:

AcmeControl1.Footer = Page.LoadTemplate([...]) 

und passieren auf dem Weg zu einer ascx Datei.

Literaturhinweise auf Templat Kontrollen erstellen, können hier gefunden werden:

ich sagte, dass es lange dauern würde.