Пользовательский контроль ASP.NET. Каков наилучший способ включения встроенной ссылки CSS только один раз?

Проблема: я внедряю файл CSS в пользовательскую библиотеку управления с несколькими элементами управления. Я хочу поделиться одним и тем же файлом CSS для всех элементов управления, независимо от того, сколько их экземпляров находится в данной форме. Если в форме больше одного элемента управления, я хотел бы точно указать ссылку на файл CSS в заголовке HTML страницы ASP.NET.

Вот что я придумал (пока):

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

Хорошо, это работает... но очевидным недостатком является использование FindControl(), чтобы увидеть, существует ли элемент управления в форме. Хотя я использую именования контейнеров, и он все еще, кажется, работает, я уверен, что есть способ нарушить это. Добавление другого элемента управления в форму с тем же идентификатором - это, безусловно, один...

Вопрос: что является лучшим способом гарантировать, что элемент управления заголовком добавляется только в заголовок HTML ровно один раз?

Примечание. Метод ClientScript.RegisterClientScriptResource() имеет параметр, который принимает тип .NET, и этот тип может использоваться для обеспечения того, чтобы код выводился только один раз на страницу. К сожалению, этот метод работает только с ссылками на файлы JavaScript. Если для ссылок CSS есть встроенный эквивалент, это было бы моим преимуществом.

Update:

Я обнаружил несколько более элегантный способ сделать это здесь с помощью page.ClientScript.RegisterClientScriptBlock и сказать ему, что вы включаете свой собственный script, однако, как указывает Рик, это не добавляет script в тег html head, а также не соответствует xhtml.

Обновление 2:

Я видел еще одну интересную идею о этой теме, но цикл через коллекцию элементов управления не очень хорошее решение и добавляет много накладных расходов, если у вас есть несколько ссылок и несколько элементов управления на странице.

Chris Lively придумал лучшее решение, требующее меньше кода. Здесь моя функция изменена с помощью нового решения:

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

Несколько вещей, чтобы отметить это решение. В своем первоначальном сообщении Крис использовал метод IsClientScriptIncludeRegistered(), который не был соответствующим методу для метода RegisterClientScriptBlock(). Чтобы сделать эту функцию правильно, тест должен выполняться с помощью IsClientScriptBlockRegistered().

Также обратите особое внимание на тип, который передается на RegisterClientScriptBlock(). Я передал собственный тип данных для этого метода (одинаково для всех моих элементов управления), но он не регистрировался таким образом, чтобы тест IsClientScriptBlockRegistered() работал. Чтобы он функционировал, текущий объект страницы должен быть передан как аргумент типа.

Хотя, по общему признанию, это решение кажется немного взломанным, оно: а) не требует большого количества кода или накладных расходов, б) создает именно желаемый результат на странице, а в) является xhtml-совместимым кодом.

Ответ 1

Чтобы предотвратить дублирование при испускании css файлов из элементов управления сервером, мы делаем следующее:

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

Сначала мы проверяем, была ли ранее зарегистрирована названная script. Если нет, добавим его.

Ответ 2

Я не знаю, почему, но ваше решение не сработало для меня, клиентский запрос ClientScript.IsClientScriptBlockRegistered всегда возвращает false. Но предложение Джона Бледзея по ссылке, которую вы уже предоставили (здесь), работало для меня:

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

Ответ 3

Почему бы просто не создать частную логическую переменную в элементе управления, которую вы установили в true при создании CSS. Затем вы можете проверить это, когда вызывается метод, чтобы проверить, был ли css уже установлен. Пример ниже (мой VB является ржавым, поэтому может быть немного неправильным)

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