2010-10-06 11 views
10

Problem: Osadzam plik CSS w niestandardowej bibliotece kontrolnej z kilkoma kontrolkami. Chcę udostępnić ten sam plik CSS dla wszystkich formantów, niezależnie od tego, ile wystąpiło ich w danym formularzu. Jeśli w formularzu znajduje się więcej niż jeden formant, chciałbym dokładnie 1 referencję do pliku CSS w nagłówku HTML strony ASP.NET.Niestandardowa kontrola programu ASP.NET - Jaki jest najlepszy sposób dołączenia odniesienia do osadzonego CSS tylko raz?

Oto co mam wymyślić (jak dotąd):

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, to działa ... ale oczywistą wadą jest tu wykorzystanie FindControl() aby sprawdzić, czy kontrola istnieje w formie . Chociaż używam nazw kontenerów i nadal wydaje się działać, jestem pewien, że jest jakiś sposób, aby to przełamać. Dodanie kolejnej kontroli w formularzu o tym samym identyfikatorze to z pewnością jeden ...

Pytanie: Jaki jest lepszy sposób na zapewnienie, że kontrola nagłówka jest dodawana do nagłówka HTML tylko raz?

Uwaga: Metoda ClientScript.RegisterClientScriptResource() ma parametr, który akceptuje typ .NET i ten typ może być używany w celu zapewnienia, że ​​kod jest wyprowadzany tylko raz na stronę. Niestety ta metoda działa tylko z odwołaniami do plików JavaScript. Jeśli istnieje wbudowany odpowiednik dla referencji CSS, byłby to mój wybór.

Aktualizacja:

odkryłem nieco bardziej elegancki sposób to zrobić za pomocą Page.ClientScript.RegisterClientScriptBlock here i mówiąc to jesteś w tym własne znaczniki skryptów, jednak jak Rick wskazuje to robi” t dodaj skrypt do znacznika html head, a także nie jest zgodny z xhtml.

Aktualizacja 2:

Widziałem inny ciekawy pomysł na this thread, ale zapętlenie poprzez zbieranie kontrole nie jest to bardzo dobre rozwiązanie i dodaje dużo napowietrznych, jeśli masz kilka odniesień i kilka formantów na stronie .

Chris Lively wymyślił lepsze rozwiązanie, które wymaga mniej kodu. Oto moja funkcja zmieniona w nowym rozwiązaniu:

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 

Kilka rzeczy na uwagę na temat tego rozwiązania. W swoim oryginalnym poście Chris użył metody IsClientScriptIncludeRegistered(), która nie była odpowiednią metodą dla metody RegisterClientScriptBlock(). Aby funkcja działała poprawnie, test należy wykonać przy pomocy IsClientScriptBlockRegistered().

Należy również zwrócić szczególną uwagę na typ przekazywany do RegisterClientScriptBlock(). Do tej metody przekazałem niestandardowy typ danych (taki sam we wszystkich kontrolach), ale nie zarejestrował się w taki sposób, aby test IsClientScriptBlockRegistered() zadziałał. Aby to działało, bieżący obiekt strony musi zostać przekazany jako argument Type.

Wprawdzie to rozwiązanie wydaje się nieco podobne do hackowania, to a) nie wymaga dużej ilości kodu ani kosztów ogólnych, b) daje dokładnie pożądane wyniki na stronie, a c) jest zgodny z XHTML.

+0

koncepcyjnej, dlaczego jest css nie jest częścią głównego pliku css witryny, które jest pobierany i buforowane przez klienta? – jball

+2

To jest biblioteka kontrolna, która teoretycznie może być używana na dowolnej stronie internetowej i wolę używać oddzielnego CSS (który może być łatwo zastąpiony) zamiast twardego kodowania wszystkich CSS inline (jak Microsoft zrobił w wielu przypadkach). Innymi słowy, moje kontrole powinny być całkowicie nieświadome witryny, w której są hostowane. – NightOwl888

+0

W3validator nie lubi niekodowanych ampersand, które zwraca GetWebResourceUrl. Aby naprawić, który zakoduje adres URL z Page.Server.HtmlEncode (url) – daniatic

Odpowiedz

9

Aby uniknąć powielania gdy emitujące pliki css z kontroli serwerów, mamy następujące:

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

Najpierw musimy sprawdzić, czy nazwany skrypt został wcześniej zarejestrowany. Jeśli nie, dodajemy to.

+0

Interesujący pomysł, i na początku myślałem, że to zadziała. Jednak próbowałem go i jest kilka problemów (chyba że coś przeoczyłem). To rejestruje CSS w typie mime "text/javascript". Dodatkowo tworzy tag "skrypt" zamiast tagu "link". Po trzecie, chociaż nie jest to tak duża oferta, znacznik nie jest zawarty w nagłówku dokumentu. – NightOwl888

+0

Ups. Wrong snippet. Spójrz ponownie. – NotMe

+0

Świetny pomysł! Dodałem poprawioną wersję tego do mojego oryginalnego wpisu. Test musi być wykonany przy pomocy RegisterClientScriptBlockInclude, ale poza tym ten kod działa świetnie. – NightOwl888

0

Po prostu nie tworzy się prywatnej zmiennej boolowskiej w formancie, który ustawiono na true, gdy pierwotnie tworzony jest CSS. Możesz to sprawdzić, gdy wywoływana jest metoda, aby sprawdzić, czy css został już ustawiony.Przykład poniżej (mój VB jest zardzewiały, więc może być Slighty źle)

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

Nie zadziała. Po pierwsze, zmienna została zmieniona na prywatną - co oznacza, że ​​każda instancja kontrolna miałaby osobną kopię. Wspólna zmienna potencjalnie może powodować problemy między różnymi użytkownikami, więc to też nie działa. – NightOwl888

+0

Masz rację NightOwl, całkowicie to przegapiłem! – WDuffy

3

Nie wiem dlaczego, ale twoje rozwiązanie nie zadziałało, wywołanie ClientScript.IsClientScriptBlockRegistered zawsze zwróciło fałsz. Ale propozycja Johna Bledsoe za odnośnik już przewidzianego (here) pracował dla mnie:

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

Jest to również ciekawy pomysł.Nie wiem, dlaczego nie myślałem o użyciu HttpContext.Items wcześniej, ale dzięki za udostępnianie. – NightOwl888

Powiązane problemy