Как получить контроль над таблицами стилей в темах ASP.NET с помощью элементов управления StylePlaceHolder и Style - PullRequest
10 голосов
/ 30 ноября 2008

Обновление: Это превратилось в сообщение в блоге с обновленными ссылками и кодом на моем блоге: https://egilhansen.com/2008/12/01/how-to-take-control-of-style-sheets-in-asp-net-themes-with-the-styleplaceholder-and-style-control/


Проблема довольно проста. При использовании тем ASP.NET вы мало что можете сказать о том, как таблицы стилей отображаются на странице.

Механизм рендеринга добавляет все таблицы стилей, которые у вас есть в папке тем, в алфавитном порядке, используя нотацию

Все мы знаем, что порядок таблиц стилей важен, к счастью, недостатки asp.nets можно обойти, добавив префиксы таблиц стилей к 01, 02, ..., 99 и, таким образом, задав нужный порядок (см. Расти Суэйн сообщение в блоге о технике для получения дополнительной информации).

Это особенно важно, если вы используете таблицу стилей сброса, которую я настоятельно рекомендую; это значительно упрощает оформление сайта в единой форме в разных браузерах (см. Сброс перезагрузки от Эрика Мейера ).

Вы также упускаете возможность указать тип носителя (например, экран, печать, проекция, шрифт Брайля, речь). И если вы предпочитаете включать таблицы стилей с помощью метода @import, вы также остаетесь в одиночестве.

Другим отсутствующим параметром является Условный комментарий, который особенно полезен, если вы используете таблицу стилей «ie-fix.css».

Прежде чем я объясню, как StylePlaceholder и Style Control решают вышеупомянутые проблемы, кредит, где кредит должен, мое решение вдохновлено За сообщение в блоге Циммермана на эту тему.

Элемент управления StylePlaceHolder находится в разделе заголовка главной страницы или страницы. Он может содержать один или несколько элементов управления стилями и по умолчанию удаляет стили, добавленные механизмом рендеринга, и добавляет свои собственные (он удаляет только стили, добавленные из текущей активной темы).

Элемент управления Style может содержать встроенные стили между открывающими и закрывающими тегами и ссылку на внешний файл таблицы стилей через свое свойство CssUrl. С другими свойствами вы управляете тем, как таблица стилей отображается на странице.

Позвольте мне показать пример. Рассмотрим простой проект веб-сайта с главной страницей и темой с тремя таблицами стилей - 01reset.css, 02style.css, 99iefix.cs. Примечание: я назвал их, используя метод префикса, описанный ранее, так как это улучшает время разработки. Кроме того, префикс тега пользовательских элементов управления «ass:».

В разделе заголовка главной страницы добавить:

<ass:StylePlaceHolder ID="StylePlaceHolder1" runat="server" SkinID="ThemeStyles" />

В каталоге вашей темы добавьте файл скина (например, Styles.skin) и добавьте следующее содержимое:

<ass:StylePlaceHolder1runat="server" SkinId="ThemeStyles">
    <ass:Style CssUrl="~/App_Themes/Default/01reset.css" />
    <ass:Style CssUrl="~/App_Themes/Default/02style.css" />
    <ass:Style CssUrl="~/App_Themes/Default/99iefix.css" ConditionCommentExpression="[if IE]" />
</ass:StylePlaceHolder1>

Вот и все. В элементе управления Style есть еще несколько свойств, которые можно использовать для управления рендерингом, но это базовая настройка. Имея это в виду, вы можете легко добавить другую тему и заменить все стили, поскольку вам нужно только включить другой файл скина.

Теперь к коду, который делает все это возможным. Я должен признать, что опыт проектирования времени имеет некоторые причуды. Вероятно, это связано с тем, что я не очень хорошо умею писать пользовательские элементы управления (на самом деле, эти две являются моими первыми попытками), поэтому мне бы очень хотелось получить информацию о следующем. В текущем проекте WCAB / WCSF, который я разрабатываю, я вижу подобные ошибки в представлении дизайна Visual Studios, и я не знаю, почему. Сайт компилируется и все работает онлайн.

Пример ошибки времени проектирования в Visual Studio http://www.egil.dk/wp-content/styleplaceholder-error.jpg

Ниже приведен код для элемента управления StylePlaceHolder:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;

[assembly: TagPrefix("Assimilated.Extensions.Web.Controls", "ass")]
namespace Assimilated.WebControls.Stylesheet
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [DefaultProperty("SkinID")]
    [ToolboxData("<{0}:StylePlaceHolder runat=\"server\" SkinID=\"ThemeStyles\"></{0}:StylePlaceHolder>")]
    [ParseChildren(true, "Styles")]
    [Themeable(true)]
    [PersistChildren(false)]
    public class StylePlaceHolder : Control
    {
        private List<Style> _styles;

        [Browsable(true)]
        [Category("Behavior")]
        [DefaultValue("ThemeStyles")]
        public override string SkinID { get; set; }

        [Browsable(false)]
        public List<Style> Styles
        {
            get
            {
                if (_styles == null)
                    _styles = new List<Style>();
                return _styles;
            }
        }

        protected override void CreateChildControls()
        {
            if (_styles == null)
                return;

            // add child controls
            Styles.ForEach(Controls.Add);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            // get notified when page has finished its load stage
            Page.LoadComplete += Page_LoadComplete;
        }

        void Page_LoadComplete(object sender, EventArgs e)
        {
            // only remove if the page is actually using themes
            if (!string.IsNullOrEmpty(Page.StyleSheetTheme) || !string.IsNullOrEmpty(Page.Theme))
            {
                // Make sure only to remove style sheets from the added by
                // the runtime form the current theme.
                var themePath = string.Format("~/App_Themes/{0}",
                                              !string.IsNullOrEmpty(Page.StyleSheetTheme)
                                                  ? Page.StyleSheetTheme
                                                  : Page.Theme);

                // find all existing stylesheets in header
                var removeCandidate = Page.Header.Controls.OfType<HtmlLink>()
                    .Where(link => link.Href.StartsWith(themePath)).ToList();

                // remove the automatically added style sheets
                removeCandidate.ForEach(Page.Header.Controls.Remove);
            }
        }

        protected override void AddParsedSubObject(object obj)
        {
            // only add Style controls
            if (obj is Style)
                base.AddParsedSubObject(obj);
        }

    }
}

И код для управления стилем:

using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;

[assembly: TagPrefix("Assimilated.Extensions.Web.Controls", "ass")]
namespace Assimilated.WebControls.Stylesheet
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [DefaultProperty("CssUrl")]
    [ParseChildren(true, "InlineStyle")]
    [PersistChildren(false)]
    [ToolboxData("<{0}:Style runat=\"server\"></{0}:Style>")]
    [Themeable(true)]
    public class Style : Control
    {
        public Style()
        {
            // set default value... for some reason the DefaultValue attribute do
            // not set this as I would have expected.
            TargetMedia = "All";
        }

        #region Properties

        [Browsable(true)]
        [Category("Style sheet")]
        [DefaultValue("")]
        [Description("The url to the style sheet.")]
        [UrlProperty("*.css")]
        public string CssUrl
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Style sheet")]
        [DefaultValue("All")]
        [Description("The target media(s) of the style sheet. See http://www.w3.org/TR/REC-CSS2/media.html for more information.")]
        public string TargetMedia
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Style sheet")]
        [DefaultValue(EmbedType.Link)]
        [Description("Specify how to embed the style sheet on the page.")]
        public EmbedType Type
        {
            get; set;
        }

        [Browsable(false)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public string InlineStyle
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Conditional comment")]
        [DefaultValue("")]
        [Description("Specifies a conditional comment expression to wrap the style sheet in. See http://msdn.microsoft.com/en-us/library/ms537512.aspx")]
        public string ConditionalCommentExpression
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Conditional comment")]
        [DefaultValue(CommentType.DownlevelHidden)]
        [Description("Whether to reveal the conditional comment expression to downlevel browsers. Default is to hide. See http://msdn.microsoft.com/en-us/library/ms537512.aspx")]
        public CommentType ConditionalCommentType
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Behavior")]
        public override string SkinID { get; set; }

        #endregion

        protected override void Render(HtmlTextWriter writer)
        {            
            // add empty line to make output pretty
            writer.WriteLine();

            // prints out begin condition comment tag
            if (!string.IsNullOrEmpty(ConditionalCommentExpression))
                writer.WriteLine(ConditionalCommentType == CommentType.DownlevelRevealed ? "<!{0}>" : "<!--{0}>",
                                 ConditionalCommentExpression);

            if (!string.IsNullOrEmpty(CssUrl))
            {               
                // add shared attribute
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");

                // render either import or link tag
                if (Type == EmbedType.Link)
                {
                    // <link href=\"{0}\" type=\"text/css\" rel=\"stylesheet\" media=\"{1}\" />
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, ResolveUrl(CssUrl));
                    writer.AddAttribute(HtmlTextWriterAttribute.Rel, "stylesheet");
                    writer.AddAttribute("media", TargetMedia);
                    writer.RenderBeginTag(HtmlTextWriterTag.Link);
                    writer.RenderEndTag();
                }
                else
                {
                    // <style type="text/css">@import "modern.css" screen;</style>
                    writer.RenderBeginTag(HtmlTextWriterTag.Style);
                    writer.Write("@import \"{0}\" {1};", ResolveUrl(CssUrl), TargetMedia);
                    writer.RenderEndTag();
                }
            }

            if(!string.IsNullOrEmpty(InlineStyle))
            {
                // <style type="text/css">... inline style ... </style>
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
                writer.RenderBeginTag(HtmlTextWriterTag.Style);
                writer.Write(InlineStyle);
                writer.RenderEndTag();
            }

            // prints out end condition comment tag
            if (!string.IsNullOrEmpty(ConditionalCommentExpression))
            {
                // add empty line to make output pretty
                writer.WriteLine();
                writer.WriteLine(ConditionalCommentType == CommentType.DownlevelRevealed ? "<![endif]>" : "<![endif]-->");
            }
        }
    }

    public enum EmbedType
    {        
        Link = 0,
        Import = 1,
    }

    public enum CommentType
    {
        DownlevelHidden = 0,
        DownlevelRevealed = 1
    }
}

Так что вы, ребята, думаете? Это хорошее решение проблемы с темой asp.net? А как насчет кода? Я действительно хотел бы получить какой-то вклад, особенно в отношении времени разработки.

Я загрузил заархивированную версию решения Visual Studio , содержащую проект, на случай, если кому-то будет интересно.

С уважением, Эгиль.

Ответы [ 3 ]

2 голосов
/ 05 декабря 2008

Нашел ответ на свой вопрос.

Причиной ошибок рендеринга, которые я получаю в режиме разработки, является очевидная ошибка в Visual Studio SP1 , которую Microsoft еще не исправила .

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

См. Ссылку выше для более подробного объяснения того, как и почему.

0 голосов
/ 21 декабря 2009

Re: используя определенный медиа-файл CSS, вы можете использовать оператор @media CSS, отлично работает.

0 голосов
/ 21 января 2009

Работает очень плавно.

Для таких, как я, которые никогда не помнят синтаксис тегов <%, вот что вам нужно добавить в начало определения главной страницы и в файл скина для регистрации пространства имен. </p>

<%@ Register TagPrefix="ass" Namespace="Assimilated.WebControls.Stylesheet" %>

Я не уверен, что хочу так много «задницы» во всем коде, но в остальном мне это нравится.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...