Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Ressourcen in CustomControl Assembly einbetten und benutzen

Einführung
Bei einem etwas umfangreicheren CustomControl kommt man schnell an die Grenzen.
Es werden zusätzlich zu reinen Textausgaben, vielfach auch Ressourcen gebraucht,
diese können in Form von Text sein, wie z.B. Javascript oder CSS, aber auch binäre Daten, wie z.B. Bilder oder ZIP Dateien.

Seit ASP.NET 2.0 gibt es die Möglichkeit, Ressourcen relativ einfach einzubinden, und zugleich in die Assembly mit zu kompilieren.
Das heisst, die DLL für das Control enthält zugleich die Ressourcen die es benutzt.
Das Deployment wird dadurch klarer und einfacher, die Ressourcen sind in der DLL gekapselt und können nicht einfach so ausgetauscht werden.
Somit kann auch das Problem vermieden werden, eine andere Version der Ressource mitzuliefern.

In ASP.NET 2.0 gibt es eine spezielle Url (WebResource.axd), die von der Engine genutzt wird, um dieses Feature umzusetzen.
Über diese Url und zwei automatisch zugewiesenen Parametern (Assembly Key / DateTime of last Assembly write) kann die Ressource abgerufen und benutzt werden.
Das ist vor allen Dingen für CSS und Javascript sehr interessant.
Anstelle von Javascript Code der im Code eingebettet und per Page.ClientScript.RegisterClientScriptBlock() registriert wird, legen wir in unserem Projekt eine normale Javascript Datei an.
Diese Datei wird als eingebettete Ressource markiert und dann per Code als externe Url in Empfang genommen.

Die Idee für das Beispielprojekt habe ich mir über zwei Ecken von Michal geholt, und den Javascript Code 1:1 übernommen.
Es wird ein CustomControl implementiert, das sich selber bei Bedarf vergrössert und als Textarea gerendert wird.
Damit haben wir nicht eine unnötig grosse Textarea auf der Seite, und den ganzen Text immer im Blickfeld.

Einbettung und Registrierung der Ressource in die Assembly
Eine Ressource (z.B. Javascript oder CSS) kann direkt als Datei in das CustomControl Projekt eingefügt werden.
Im Beispielprojekt erstellen wir eine Javascript Datei im Solution Explorer und markieren diese als eingebettete Ressource.
Dies bewirkt dass die Ressource beim kompilieren in die resultierende DLL eingebettet wird.

Die Ressource muss in der AssemblyInfo.cs registriert werden, damit wir über den Webresource.axd HTTP Handler darauf zugreifen können.
Unter "Properties" im Solution Explorer und dem betreffenden Projekt, liegt die AssemblyInfo.cs, in dieser müssen wir ein zusätzliches Attribut nach folgender Konvention hinzufügen.

[assembly: WebResource("<Root Namespace>.<Dateipfad>", "<ContentType>")]

 

Im Falle des Beispielprojekts ergibt sich folgendes Attribut:

[assembly: WebResource("t4m.Controls.AutoResizingTextarea.js", "text/javascript")]

Update:

In VB.NET gibt es die Option einen "Rootnamespace" bzw. "Stamm Namespace" anzugeben, oder auch nicht.
Wenn dieser leer ist, darf _nur_ der Dateiname der Ressource angegeben werden.

Andernfalls [Stamm Namespace].[Dateiname]

VB.NET kann nicht mit Pfaden umgehen, daher müssen alle Ressourcen jeweils einen anderen Dateinamen in derselben Assembly haben,
ansonsten wird die Assembly nicht erstellt.

Zitat aus (CodeProject Artikel [Kommentar]):

c# way:
[Default namespace].[PathToTheResource].[ResourceName]

vb.net way:
If you have empty namespace: [ResourceName] Nothing more nothing less!!!
You have a given root namespace: [Default namespace].[ResourceName] No path at all!!!

So vb.net doesn't (cannot?) use the path, this means: if you have two resource with same name in different place in your solution the project won't build!

Eine Angabe in VB.NET kann in der Controlklasse erfolgen, sowie auch in der AssemblyInfo.vb.
Beides funktioniert, allerdings wäre der Weg über die AssemblyInfo.vb wohl sauberer.
Ein solches Attribut kann folgendermassen aussehen:

<Assembly: WebResource("MeineRessource.js", "text/javascript")>

(Damit die AssemblyInfo.vb sichtbar wird, muss im Solution Explorer die Option "Alle Dateien anzeigen" gewählt werden!)

Benutzung der Ressource im CustomControl
Über die ClientScriptManager Instanz und dessen Methode "GetWebResourceUrl" kann die Url zur Ressource abgerufen werden.
Mit der Methode "RegisterClientScriptInclude" des ClientScriptManagers und der Url wird die Ressource als externes Javascript eingebunden, und ist somit auf der aktuellen Seite verfügbar.

In der überschriebenen Methode "AddAttributesToRender" hängen wir ein "onkeyup" Eventhandler mit einem parameterisierten Funktionsaufruf als Attribut für die Textarea hinzu.

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
    string attributeString = String.Empty;
    bool addAttributes = true;
   
    if(this._minRows != 0 && this._minCols != 0) {
        // Zeilen und Spalten resizen
        attributeString = "resizeTextArea(this, " + this._minCols.ToString() + ", "
                          + this._minRows.ToString() + ");";
    } else if(this._minCols != 0) {
        // Nur Zeilen resizen
        attributeString = "resizeTextArea(this, "
                          + this._minCols.ToString() + ");";
    } else {
        addAttributes = false;
    }
   
    if(addAttributes)
        writer.AddAttribute("onkeyup", attributeString);
   
    base.AddAttributesToRender(writer);
}

 

Im überschriebenen OnPreRender des Controls wird das Include per ClientScriptManager registriert.
Die Methode "RegisterClientScriptInclude" erwartet einen Schlüssel und die Url der Resource.
Die Parameter der "GetWebResourceUrl" Methode sind jeweils der Typ der Assembly und den Ressourcennamen (der selbe wie in AssemblyInfo.cs).

 
protected override void OnPreRender(EventArgs e)
{
    if (this.Page != null)
    {
        ClientScriptManager manager = this.Page.ClientScript;
        manager.RegisterClientScriptInclude("AutoResizingTextArea",
                                            manager.GetWebResourceUrl(this.GetType(),
                                                                      "t4m.Controls.AutoResizingTextarea.js"));
    }
   
    base.OnPreRender(e);
}

 

Mithilfe der "RegisterClientScriptInclude" Methode wird der Javascript Include Tag nur einmal und inmitten der Seite bzw. genau oberhalb des ersten Controls gerendert.
Das ist ein wenig unschön. Das Include kann aber auch in den Head Tag gerendert werden, jedoch in Eigenregie.
Über die ID und vorangehende Prüfung auf ein Control in der Hierarchie wird vermieden, dass das Include bei mehr als einem Control des gleichen Typs mehrfach gerendert wird.
Diese Überprüfungsarbeit wird uns im oberen Fall mit "RegisterClientScriptInclude" abgenommen, aber die Prüfung auf die ID ist nicht aufwändig und funktioniert.

if (this.Page.FindControl("AutoResizingTextArea") == null)
{
    string jsUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "t4m.Controls.AutoResizingTextarea.js");
    HtmlGenericControl link = new HtmlGenericControl("script");
    link.ID = "AutoResizingTextArea";
    link.Attributes.Add("type", "text/javascript");
    link.Attributes.Add("src", jsUrl);
    this.Page.Header.Controls.Add(link);
}

 

Die Ausgabe des Javascript Includes sieht dann z.B. so aus:

</script src="http://www.aspnetzone.de/WebResource.axd?d=mKz7Vb-Q8_bkDUZyl5AYY8EyVDVf_p6szFgppsuGhxxVgB7LsNffB2soCdnTCu7p8Y2KMjUlABwuU4ycvynCQw2&amp;t=633201523485135647" type="text/javascript">

Quellen und weiterführende Links:

Beispielprojekt:

Bearbeitung / Korrekturen:
26.06.08 - Update für VB.NET

Veröffentlicht Sonntag, 15. Juli 2007 23:54 von Peter Bucher

Kommentare

# re: Ressourcen in CustomControl Assembly einbetten und benutzen

Sehr nett und anschaulich geschrieben.

Danke dir Peter :)

Donnerstag, 19. Juli 2007 12:00 by plongo_291

# Alles neu oder was?

Info &amp; Statistiken Ja, es gibt Neuigkeiten auf meinem Blog, aber es ist nicht alles neu ;-) Unter

Freitag, 24. August 2007 01:45 by Peter Bucher

# Die Sache mit dem Zeilenumbruch, oder: Auf Kriegspfad mit IE6

Bereits Gestern kam die Beschwerde rein, das eine Funktion basierend auf Javascript einer Webseite nicht

Montag, 12. November 2007 14:44 by Peter Bucher

# re: Ressourcen in CustomControl Assembly einbetten und benutzen

Hallo Peter,

ich musste mich aktuell auch mit dem Thema Webresourcen auseinandersetzen, daher vielen Dank für deinen Artikel, der bei mir für Klarheit sorgte. Ich meine allerdings eine kleine Unklarheit gefunden zu haben, im Assembly.cs-file und beim GetWebResourceUrl gibst Du als Konvention zur Bezeichnung der Ressource "<Root Namespace>.<Dateipfad>" an. In meinem kleinen Projekt ergab es sich, dass der Name des Assemblies mit dem Namespace nicht übereingestimmt hat und dann die Webresource der Anwendung zur Laufzeit nicht zur Verfügung stand. Nach etwas "Rumsuchen und -probieren" glaube ich, dass es allgemeingültig "<Name des Assembly>.<Dateipfad>" lauten sollte.

Beste Grüße Ralf Jörg

Montag, 14. April 2008 11:21 by RalfJoerg

# re: Ressourcen in CustomControl Assembly einbetten und benutzen

Hallo Ralf

Danke für deinen Kommentar.

In meinem Beispiel stimmt der Assemblyname aber auch nicht mit dem Namespace überein...

Ausserdem ist das die Konvention die ich auf allen Seiten gesehen habe und auch funktioniert.

Kannst du mir mal ein Beispiel mit dieser Konvention schicken, das nicht funktioniert?

Freitag, 25. April 2008 04:07 by Peter Bucher

# ASP.Net: (Bild/Script) Ressourcen in Assemblies einbetten und benutzen

ASP.Net: (Bild/Script) Ressourcen in Assemblies einbetten und benutzen

Freitag, 13. März 2009 11:44 by www.babblr.de

# re: Ressourcen in CustomControl Assembly einbetten und benutzen

Hallo Peter,

vielen Dank für diese wertvolle Information das man in VB.NET die Resource nur mit [Root Namespace].[ResourceName] angibt. Hier habe ich schon über 1,5 Stunden gesucht, aber die meinsten Beispielen sind in C# und beachten VB.NET weniger.

Gruß

Andreas

Dienstag, 21. April 2009 10:39 by plongo_291

# re: Ressourcen in CustomControl Assembly einbetten und benutzen

hi,

ich bin immer wieder am überlegen, wie ich am besten Bilder zentral in meine WebApp einbinden kann und deren URL ich dann problemlos nutzen kann - ohne viel hin und her mit '~' vergessen, etc... . Ich hatte auch bereits den Gedanken diese Bilder als WebResource in eine Assembly einzubinden. Relativ nervig ist es jedoch, wenn ich z.B. 40 Bilder habe für jedes dieser Bilder die Zeile [assembly: WebResource("<Root Namespace>.<Dateipfad>", "<ContentType>")] einzufügen. Kann ich das auch programmatisch machen? Ich könnte mir vorstellen, dass das ich über Reflection implementieren kann... Oder gibt es andere gängige Methoden Bilder in einer WebAnwendung zur Verfügung stellen?

mfg

xforfun

Montag, 1. Februar 2010 13:48 by xforfun

# re: Ressourcen in CustomControl Assembly einbetten und benutzen

Salute xforfun

Du kannst höchstens die Einträge in der Assembly.cs / .vb generieren lassen.

T4 würde sich dazu anbieten und wäre simpel.

Grüsse Peter

Dienstag, 2. Februar 2010 09:01 by Peter Bucher
Anonyme Kommentare sind nicht zugelassen