Mittwoch, Juni 06, 2007 |
Print

Custom Controls in ASP.NET

Neben den User Controls kann man in ASP.NET auch sogenannte Custom Controls verwenden. Die Unterschiede will ich hier kurz anführen:

User Control

Custom Control

Einfach zu erstellen Schwierig zu erstellen
Für jede Applikation ist eine Kopie erforderlich Kann in den "Global Assembly Cache" geladen werden und ist damit für alle Applikationen verfügbar
Gut für einfache Controls Gut für komplexe Controls
Gut, wenn viele statischen Elemente verwendet werden Gut wenn viele dynamischen Elemente verwendet werden (wie z.B. Tabellen aus einer Datenbank oder Suchergebnisse)
Kann dynamisch zur Laufzeit kompiliert werden - kein Codebehindfile wird benötigt Muss kompiliert werden
 
 
 
 using System; 
 using System.Web.UI; 
  
 namespace CustomControls 
 { 
     public class Login : Control 
     { 
  
         private string RenderPage( ) 
         { 
             return "<div>Hello World</div>"; 
         } 
  
         protected override void CreateChildControls( ) 
         { 
             Controls.Add( new LiteralControl( this.RenderPage( ) ) ); 
             this.DataBind( ); 
         } 
     } 
 } 
  
  

 Der sicherlich größte Unterschied ist, dass Custom Controls kompliliert werden müssen, damit man sie verwenden kann.
Kompilieren wir also unser Login.cs gerüst mit folgendem Befehl auf der Kommandozeile:

    csc /target:library /out:bin\CustomControls.dll Login.cs

Fügen wir nun als die Control in unsere website ein. Damit die Seite weiß, wo sie die Control findet verwenden wir wieder eine "Register" Anweisung ganz oben auf der Seite. Diese Register Anweisung sieht ähnlich aus wie die, die wir bereits von den User Controls her kennen:

    <%@RegisterTagPrefix="CustomControls" NameSpace="CustomControls" Assembly="CustomControls" %>



Das Attribut "TagPrefix" bezeichnet einen eindeutigen namespace für die UserControl , der "Namespace" ist der Namespace, der in der Control verwendet wird.
Das Attribut "Assembly" bezeichnet den Namen der Assembly, in der unsere Control liegt.

Wenn eine Control auf diese Art registriert ist kannst du sie - gleich wie die Server und User Controls an einer beliebigen Stelle einfügen. In unserem Fall sieht das dann so aus:

    <CustomControls:Loginid="MyLogin" runat="server"/>

 Deine default.aspx sollte jetzt in etwa so aussehen:


    <%@RegisterTagPrefix="CustomControls" NameSpace="CustomControls" Assembly="CustomControls" %>

    <html>
        <head>
            <title>Meine erste ASP.NET Seite</title>
        </head>

        <body>
            <CustomControls:Loginid="MyLogin" runat="server"/>
        </body>
    </html>



Öffne sie in deinem Browser. Achte darauf, dass du sie von deinem Webserver aus öffnest und das der Ordner als Applikation auf deinem Webserver eingerichtet ist.

Die Seite sollte ein "Hello World" anzeigen und sonst weiss sein. Deine erste Usercontrol ist fertig!

Erhöhen wir nun die Komplexität der Control. Wir wollen immer noch unser Loginfenster darstellen, davon sind wir noch etwas entfernt.

Um unserer Control Werte zu übergeben verwenden wir Properties. Hier nur zwei Beispiele, einmal die Überschrift und einmal die CSS Klasse:

 
         private string cssClass = ""; 
         private string text = ""; 
  
         public string CssClass  
         { 
             get 
             { 
                 return this.cssClass; 
             } 
             set 
             { 
                 this.cssClass = " style=\"" + value + "\""; 
             } 
         } 
  
         public string Text  
         { 
             get 
             { 
                 return ( this.text != "" ) ? this.text : "Please enter your credentials to proceed"; 
             } 
             set 
             { 
                 this.text = value; 
             } 
         } 
  
  

 

Erzeuge folgende Properties, ihre Variablen und überlege dir einen logischen Rückgabewert:


  • CssClass - das class Attribut für unser Loginfenster
  • Text - die Überschrift
  • Name - der Text, der vor der Loginnamen-textbox angezeigt wird
  • Password - der Text, der vor der Passwort-textbox angezeigt wird
  • ButtonText - der Text, der in dem loginbutton angezeigt wird

Nun modifizieren wir die PageRender( ) Methode sodass sie uns den Text, die Textboxen und den Button ausgibt. Sie könnte in etwa so aussehen:

 
     private string RenderPage( ) 
         { 
             return "<div" + this.CssClass + ">" + this.Text + "<br />" + 
             this.Name + " <input id=\"" + this.UniqueID + "\" name=\"" +  
             this.UniqueID + "\" type=\"text\"><br />" + 
             this.Password + " <input id=\"" + this.UniqueID + "\" name=\"" +  
             this.UniqueID + "\" type=\"password\"><br />" +  
             "<input type=\"submit\" value=\"" + this.ButtonText + "\"></div>"; 
         } 
  
  

UniqueID ist eine ID, die wir unseren Textboxen zuweisen müssen, damit wir die Werte später aus den Daten, die bei einem Postback übergeben werden, auslesen können.

Kompliliere nun wieder unsere Control und öffne wieder deine default.aspx. Die Felder werden nun angezeigt und die Standardtexte aus deinen Properties werden jetzt angezeigt. Wir können diese Texte in der default.aspx verändern, indem wir unserem Control-tag die entsprechenden Attribute hinzufügen.
Ein Beispiel (unter Verwendung einer CSS-Klasse Namens "MyLoginClass":


    <CustomControls:Loginid="MyLogin"
            CssClass="MyLoginClass"
            Text="Please LogIn"
            Name="Enter your Name:"
            Password="Enter your Password"
            ButtonText="Login"
            runat="server"/>



Hinzufügen von Events

Wenn wir auf unserer Seite unsere Daten eingeben passiert momentan noch nichts. Das wollen wir nun ändern.
Damit wir Events abarbeiten können müssen wir folgende Änderungen an unserem Login.cs file vornehmen:

 
 // Änderungen in rot 
  
 using System; 
 using System.Collections.Specialized; 
 using System.Web.UI;
  
  
 namespace CustomControls 
 { 
     public class Login : Control , IPostBackDataHandler  
     { 
  
         /* Diese Variabel speichert unsere Logindaten 
             in der Form "Name,Passwort" */
 
         public string LoginData 
         { 
             get 
             { 
                 return (ViewState["lgdt"]  != null) ? ViewState["lgdt"] .ToString() : ""; 
             } 
             set 
             { 
                 ViewState["lgdt"]  = value; 
             } 
         } 
  
         void IPostBackDataHandler.RaisePostDataChangedEvent() 
         { 
             // Brauchen wir hier nicht 
         } 
  
         bool IPostBackDataHandler.LoadPostData(string postDataKey,  
                         NameValueCollection postCollection) 
         { 
             string value = postCollection[postDataKey] ; 
  
             if ( value != null && value != "" ) 
             { 
                 // Speichere unsere Logindaten in der 
                 // Form "Name,Passwort" 
                 this.LoginData = ( value ); 
                 return true;  
             } 
  
             return false; 
         }
  
  
         // Die anderen Properties in unserer Control 
         //     und eine neue, die den Text für eine erfolgreiche 
         //     authetifizierung bedeutet (Authenticate) 
  
         protected override void CreateChildControls() 
         { 
             // Als Benutzername nehmen wir "User", als 
             //     Passwort "Passwort" 
             if ( this.LoginData == "User,Passwort" ) 
                 Controls.Add( new LiteralControl( this.Authenticate ) ); 
             else
  
                 Controls.Add( new LiteralControl( this.RenderPage( ) ) ); 
             this.DataBind(); 
         } 
  
     } 
 } 
    
Nun verschwindet die Loginfläche - vorausgesetzt der Benutzer ist authetifiziert - und wird ersetzt durch die Erfolgsmeldung.

Anstelle der Meldung könnte natürlich jetzt auch etwas anderes geladen werden.

Templates in Custom Controls

Um deine Control möglichst flexibel zu machen kannst du dem Entwickler, der deine Control benutzt ein Templateset zur verfügung stellen, mit dem er festlegen kann, wie das Loginfeld auszuschauen hat.
Dafür müsst du eine neue Datei erstellen. Wir nennen sie "LoginTemplate.cs". Diese Klasse wird von uns als Container für das Template verwendet. Es beinhaltet in unserem Beispiel nur properties, die gelesen werden können (Properties ohne set) und einem Konstruktor, der einen Pointer auf unsere Login Klasse als Parameter hält.
Außerdem implemetiert sie das Interface "INamingContainer"

Im großen und ganzen wird diese Klasse also etwa so aussehen:

 
 using System; 
 using System.Web.UI; 
  
 namespace CustomControls 
 { 
     public class LoginTemplate : Control, INamingContainer 
     { 
         // Unsere Loginklasse 
         private Login parent; 
  
         // Eine Beispiels - Property 
         public string UniqueID 
         { 
             get 
             { 
                 return parent.UniqueID; 
             } 
         } 
  
         // Alle anderen Properties .. 
  
         public LoginTemplate( Login parent ) 
          { 
             this.parent = parent; 
         } 
     } 
 } 
    
Beachte, dass du nun beim kompilieren auch diese Datei anhängen musst!

Auch unsere Login Klasse müssen wir leicht modifizieren.
Über der Klassendefintion müssen ein Compilerstatement anfügen und auch hier INamingContainer implementieren:

 
 [
 ParseChildren( "true" ) 
 ]  
 public class Login : Control, IPostBackDataHandler, INamingContainer 
  
  

 Zum einen benötigen wir nun eine Variable, die unser Template beeinhalt und wir müssen auch in der Lage sein ein solches Template zu befüllen. Das Lösen wir über zwei Properties. Die eine enthält eine Control die wir benutzen werden, um das Template zu laden, die andere ist vom Typ ITemplate und wird verwendet um die Properties an die Control zu übergeben und die Control damit zu füllen.
Das Ganze sieht dann ungefähr so aus:

 
         private ITemplate myLoginTemplate; 
         private Control myLoginContainer; 
  
         [TemplateContainer(typeof( LoginTemplate ) ) ]  
         public ITemplate MyLoginTemplate 
         { 
             get 
             { 
                 return myLoginTemplate; 
             } 
             set 
             { 
                 myLoginTemplate = value; 
             } 
         } 
  
         public Control MyLoginContainer 
         { 
             get 
             { 
                 return myLoginContainer; 
             } 
         } 
  
         protected override void CreateChildControls() 
         { 
             if ( this.LoginData == "User,Passwort" ) 
                 Controls.Add( new LiteralControl( this.Authenticate ) ); 
             else if ( this.MyLoginTemplate != null ) 
             { 
                 this.myLoginContainer = new LoginTemplate( this ); 
                 this.MyLoginTemple.InstantiateIn( this.MyLoginContainer ); 
                 Controls.Add( this.MyLoginContainer ); 
                 this.DataBind( ); 
             }
  else { 
                 Controls.Add( new LiteralControl( this.RenderPage( ) ) ); 
                 this.DataBind(); 
             } 
         } 
     } 
  
  

 Jetzt kann man in der Datei default.aspx ein Template folgendermaßen definieren:


    <CustomControls:Loginid="MyLogin"
            CssClass="MyLoginClass"
            Text="Please LogIn"
            Name="Enter your Name:"
            Password="Enter your Password"
            ButtonText="Login"
            runat="server">
                <MyLoginTemplate>
                    <table>
                        <tr>
                            <tdcolspan="2">
                                <%#Container.Text %>
                            </td>
                        </tr><tr>
                            <td>
                                <%#Container.Name %>
                            </td>
                            <td>
                                <input
                                        id='<%#Container.UniqueID %>'
                                        name='<%#Container.UniqueID %>'
                                        type="text"/></td>
                        </tr><tr>
                            <td>
                                <%#Container.Password %>
                            </td>
                            <td>
                                <input
                                        id='<%#Container.UniqueID %>'
                                        name='<%#Container.UniqueID %>'
                                        type="password"/></td>
                        </tr><tr>
                            <tdcolspan="2" align="left">
                                <inputtype="submit"
                                         value='<%#Container.Text %>'/></td>
                        </tr>
                    </table>
                </MyLoginTemplate>
    </CustomControls:Login>

Die Container Items sind die Properties, die du davor in der Klasse LoginTemplate festgelegt hast.
Jetzt musst du die Applikation nur noch compilieren und fertig ist deine Web Custom Control!