2010-10-06 5 views
10

Le problème: j'incorpore un fichier CSS dans une bibliothèque de contrôle personnalisée avec plusieurs contrôles. Je veux partager le même fichier CSS pour tous les contrôles, quel que soit le nombre d'instances sur un formulaire donné. Lorsque plus d'un contrôle est sur le formulaire, je voudrais exactement 1 référence au fichier CSS dans l'en-tête HTML de la page ASP.NET.ASP.NET Custom Control - Quelle est la meilleure façon d'inclure une seule fois une référence CSS incorporée?

Voici ce que je suis venu avec (à ce jour):

Public Sub IncludeStyles(ByVal Page As System.Web.UI.Page) 
    'Don't include the reference if it already exists... 
    If Page.Header.FindControl("MyID") Is Nothing Then 
     Dim cssUrl As String = Page.ClientScript.GetWebResourceUrl(GetType(Common), StylePath) 

     Dim css As New System.Web.UI.HtmlControls.HtmlGenericControl("link") 
     With css 
      .Attributes.Add("rel", "stylesheet") 
      .Attributes.Add("type", "text/css") 
      .Attributes.Add("href", cssUrl) 
      .ID = "MyID" 
     End With 

     Page.Header.Controls.Add(css) 
    End If 
End Sub 

Ok, ça marche ... mais la faille évidente est l'utilisation ici de FindControl() pour voir si le contrôle existe sur la forme . Bien que j'utilise des conteneurs de dénomination et qu'il semble toujours fonctionner, je suis sûr qu'il y a un moyen de rompre cela. Ajout d'un autre contrôle sur le formulaire avec le même ID est sûrement un ...

La question: Quelle est une meilleure façon de s'assurer que le contrôle d'en-tête est seulement ajouté à l'en-tête HTML une seule fois?

Remarque: La méthode ClientScript.RegisterClientScriptResource() possède un paramètre qui accepte un type .NET et ce type peut être utilisé pour garantir que le code est uniquement sorti une fois par page. Malheureusement, cette méthode ne fonctionne qu'avec les références de fichiers JavaScript. S'il y a un équivalent intégré pour les références CSS, ce serait ma préférence.

Mise à jour:

j'ai découvert une façon de le faire here un peu plus élégant en utilisant Page.ClientScript.RegisterClientScriptBlock et lui disant que vous incluez vos propres balises de script, mais comme le souligne Rick out, ce n » t ajoute le script à la balise head html et n'est pas non plus compatible xhtml.

Mise à jour 2:

je vis une autre idée intéressante sur this thread, mais une boucle à travers la collection de contrôles n'est pas une très bonne solution et ajoute beaucoup de frais généraux si vous avez plusieurs références et plusieurs contrôles sur la page . Chris Lively a proposé une meilleure solution qui nécessite moins de code. Voici ma fonction altérée avec la nouvelle solution:

Public Sub IncludeStyles(ByVal Page As System.Web.UI.Page) 
    If Not Page.ClientScript.IsClientScriptBlockRegistered(StyleName) Then 
     Dim cssUrl As String = Page.ClientScript.GetWebResourceUrl(GetType(Common), StylePath) 

     Dim css As New System.Web.UI.HtmlControls.HtmlGenericControl("link") 
     With css 
      .Attributes.Add("href", cssUrl) 
      .Attributes.Add("rel", "stylesheet") 
      .Attributes.Add("type", "text/css") 
     End With 

     Page.Header.Controls.Add(css) 
     Page.ClientScript.RegisterClientScriptBlock(GetType(Page), StyleName, "") 
    End If 
End Sub 

Un couple de choses à noter à propos de cette solution. Dans son article d'origine, Chris a utilisé la méthode IsClientScriptIncludeRegistered() qui n'était pas la méthode correspondante à la méthode RegisterClientScriptBlock(). Pour que cette fonction fonctionne correctement, le test doit être effectué avec IsClientScriptBlockRegistered().

En outre, faites très attention au type qui est passé à RegisterClientScriptBlock(). J'ai passé un type de données personnalisé à cette méthode (le même pour tous mes contrôles), mais il ne s'est pas enregistré de telle sorte que le test IsClientScriptBlockRegistered() puisse fonctionner. Pour qu'il fonctionne, l'objet Page en cours doit être passé en tant qu'argument Type.

Bien que cette solution ressemble un peu à un hack, elle a) ne nécessite pas beaucoup de code ou de surcharge, b) produit exactement la sortie désirée sur la page, et c) est un code compatible xhtml.

+0

Conceptuellement, pourquoi le css ne fait pas partie du principal fichier css du site qui est récupéré et mis en cache par le client? – jball

+2

Ceci est une bibliothèque de contrôle qui peut théoriquement être utilisée sur n'importe quel site web et je préfère utiliser un CSS séparé (qui peut facilement être surchargé) plutôt que de coder tout le CSS en ligne (comme Microsoft l'a fait dans de nombreux cas). En d'autres termes, mes contrôles doivent être totalement ignorants du site dans lequel ils sont hébergés. – NightOwl888

+0

Le w3validator n'aime pas l'esperluette non codée renvoyée par GetWebResourceUrl. Pour réparer cela encoder l'URL avec Page.Server.HtmlEncode (url) – daniatic

Répondre

9

Pour éviter la duplication lors de l'émission des fichiers CSS des contrôles de serveur, nous procédez comme suit:

if (!Page.ClientScript.IsClientScriptBlockRegistered("SoPro." + id)) { 
    HtmlLink cssLink = new HtmlLink(); 
    cssLink.Href = cssLink.Href = Page.ClientScript.GetWebResourceUrl(this.GetType(), styleSheet); 
    cssLink.Attributes.Add("rel", "stylesheet"); 
    cssLink.Attributes.Add("type", "text/css"); 
    this.Page.Header.Controls.Add(cssLink); 
    this.Page.ClientScript.RegisterClientScriptBlock(typeof(System.Web.UI.Page), "SoPro." + id, String.Empty); 
} 

Nous vérifions d'abord si le script nommé a été précédemment enregistré. Sinon, nous l'ajoutons.

+0

Idée intéressante, et au début je pensais que ça marcherait. Cependant, j'ai essayé et il y a quelques problèmes (sauf si j'ai raté quelque chose). Cela enregistre le CSS sous le type mime "text/javascript". De plus, il crée une balise "script" au lieu d'une balise "link". Troisièmement, même si l'affaire n'est pas aussi importante, l'étiquette n'est pas incluse dans la tête du document. – NightOwl888

+0

Oups. Mauvais extrait. Regarde encore. – NotMe

+0

Bonne idée! J'ai ajouté une version corrigée de ceci à mon message original. Le test doit être fait avec RegisterClientScriptBlockInclude, mais à part que ce code fonctionne très bien. – NightOwl888

0

Pourquoi ne pas simplement créer une variable booléenne privée dans le contrôle que vous avez défini sur true lors de la création initiale du CSS. Vous pouvez ensuite vérifier cela lorsque la méthode est appelée pour voir si le CSS a déjà été défini.Exemple ci-dessous (mon VB est rouillé donc peut-être tort slighty)

Private _hasCss As Boolean = False 

Public Sub IncludeStyles(ByVal Page As System.Web.UI.Page) 
    'Don't include the reference if it already exists... 
    If Not _hasCss Then 
     Dim cssUrl As String = Page.ClientScript.GetWebResourceUrl(GetType(Common), StylePath) 

     Dim css As New System.Web.UI.HtmlControls.HtmlGenericControl("link") 
     With css 
      .Attributes.Add("rel", "stylesheet") 
      .Attributes.Add("type", "text/css") 
      .Attributes.Add("href", cssUrl) 
      .ID = "MyID" 
     End With 

     Page.Header.Controls.Add(css) 
     _hasCss = True 

    End If 
End Sub 
+0

Ne va pas travailler. Pour un, vous avez rendu la variable private - ce qui signifie que chaque instance de contrôle aurait une copie distincte. Une variable partagée aurait potentiellement des problèmes entre différents utilisateurs, donc cela ne fonctionnerait pas non plus. – NightOwl888

+0

Vous avez raison NightOwl, j'ai totalement raté ça! – WDuffy

3

Je ne sais pas pourquoi, mais votre solution ne fonctionnait pas pour moi, l'appel ClientScript.IsClientScriptBlockRegistered renvoyait toujours false. Mais la suggestion de John Bledsoe sur le lien que vous avez déjà fournies (here) a fonctionné pour moi:

public static void IncludeStylesheet(Page page, string href, string styleID) 
{ 
    //Prevent the stylesheet being included more than once 
    styleID = "_" + styleID; 
    if (HttpContext.Current.Items[styleID] == null) 
    { 
     HtmlLink htmlLink = new HtmlLink(); 
     htmlLink.Href = href; 
     htmlLink.Attributes.Add("rel", "stylesheet"); 
     htmlLink.Attributes.Add("type", "text/css"); 
     page.Header.Controls.Add(htmlLink); 
     HttpContext.Current.Items[styleID] = true; 
    } 
} 
+0

C'est une idée intéressante aussi, je ne sais pas pourquoi je n'ai pas pensé à utiliser le HttpContext.Items avant, mais merci pour le partage. – NightOwl888

Questions connexes