如何使用 StylePlaceHolder 和 Style 控件控制 ASP.NET 主题中的样式表

2023-11-24

Update:这变成了一篇博客文章,包含更新的链接和代码,位于我的博客上: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 来规避,从而强制您想要的顺序(参见 Rusty Swayne博客文章有关该技术的更多信息)。

如果您使用重置样式表,这一点尤其重要,我强烈推荐这样做;它使得跨浏览器以一致的形式设计网站变得更加容易(看看埃里克·迈耶 (Eric Meyer) 重装重置).

您还无法指定媒体类型(例如屏幕、打印、投影、盲文、语音)。如果您更喜欢使用 @import 方法包含样式表,那么您也会受到冷落。

另一个缺少的选项是条件注释,如果您使用“ie-fix.css”样式表,它特别有用。

在我解释 StylePlaceholder 和 Style 控件如何解决上述问题之前,我的解决方案的灵感来自于根据齐默尔曼的博客文章就此主题而言。

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 Studio 设计视图中看到类似的错误,但我不知道为什么。网站编译完毕,一切都可以在线运行。

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 解决方案的压缩版本包含该项目,以防有人感兴趣。

最好的问候,埃吉尔。


找到了我自己问题的答案。

我在设计模式下遇到渲染错误的原因是 Visual Studio SP1 中的一个明显错误,微软尚未修复.

因此,只要您只是将自定义控件包含在预编译的程序集中,而不是通过同一解决方案中的另一个项目,上述代码在设计模式下也可以按预期工作。

有关如何以及为何的更详细说明,请参阅上面的链接。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 StylePlaceHolder 和 Style 控件控制 ASP.NET 主题中的样式表 的相关文章

  • 由于固定导航,增加了 FancyBox v2 的顶部和底部边距

    我目前正在开发一个网站 该网站将来将具有响应能力 该网站主要由图像组成 单击这些图像会加载到 FancyBox 中 FancyBox v2 现在具有响应能力 因此可以在屏幕尺寸发生变化时重新调整图像等的大小 作为我设计的一部分 我有两个固定
  • 文本后面有粗下划线

    如何使用 span 和 css 在文本 ABC 后面重现这种下划线 我已经能够做下划线below嵌套的文本span和彩色的border bottom 但无法获取behind图像和above文本基线 p style font size 48p
  • 从呈现的控件 ID 中删除 ctl00$ContentBody$

    我对现有的应用程序进行了一些更改 该应用程序以前只是简单的 HTML 和 Javascript 为了添加服务器端功能 我选择了 ASP NET 并利用了母版页概念 不幸的是 在一个巨大的 Web 表单上 控件 ID 全部被 ctl00 Co
  • 如何使用 ASP.NET Web 表单从代码隐藏中访问更新面板内的文本框、标签

    我在更新面板中定义了一些控件 它们绑定到中继器控件 我需要根据匿名字段隐藏和显示用户名和国家 地区 但问题是我无法以编程方式访问更新面板中定义的控件 我如何访问这些控件 我也在网上查找但找不到很多参考资料 下面是来自aspx页面和 cs页面
  • 是否有针对 CSS 位置和可点击区域的 Android 浏览器错误的解决方法?

    当您有一些可点击的内容时 例如 a a
  • 在 IE 8 及以下版本中动态添加 @font-face 规则

    我使用 IE stylesheet addRule 方法添加 font face 规则 但是 符号对于该方法的 选择器 参数来说是不允许的字符 因此我收到 无效参数 错误 s addrule font face font family Fo
  • 如何在ASP.NET服务器的web.config文件中使用Azure Pipeline中设置的环境变量?

    我打算在 ASP NET 中使用 Azure Pipeline 中的以下环境变量web config file
  • IE7 显示问题:菜单中的表格

    我写了一个菜单样式 在 IE8 FF3 6 GC7 中运行良好 现在的问题是 我的老板希望它甚至可以在 IE7 上运行 我真的很努力地让它在 IE7 上运行 但无法获得相同的外观 menu css a outline none menu m
  • 基于网络的应用程序中的图表

    可使用 ASP NET 在网页上显示图表的各种图表工具有哪些 我了解 Dundas 和 Infragistics 等商业工具 我可以用谷歌搜索这个 但我想知道参与者使用过的各种工具 任何可用的免费图表工具也欢迎提及 如果您不介意使用 Fla
  • 删除数据表列中的额外填充

    你好 我创建了 JQuery DataTables 如下所示 所以我的问题是如何删除 图片 列中过多的填充 这就是我初始化表的方式 violators tbl DataTable aoColumnDefs bSortable false a
  • ASP.NET预编译的优点是什么?

    使用 Aspnet compiler exe 代替通过 Visual Studio 进行的传统发布有多有用 那么资源 resx 文件又如何呢 与简单的 xcopy 相比 预编译有两个主要优点 文件系统不会包含所有代码 aspx文件和后面的所
  • 序列包含多个元素

    我在通过 Linq 获取 RhsTruck 类型的列表并显示它们时遇到一些问题 RhsTruck 只有属性 品牌 型号 序列号 等 RhsCustomer 具有 CustomerName CustomerAddress 等属性 我不断收到错
  • 使用 Kentor.AuthServices.StubIdp 作为生产 IDP

    我正在尝试在我的应用程序中实现 IDP SAML2 服务器 鉴于我的应用程序拥有所需的所有数据 我不希望我的任何合作伙伴要求我们的客户在他们这边注册 我对 SAML2 协议不是很熟悉 我找到了这个项目Kentor AuthServices
  • 同时从2个表中删除?

    我正在使用 asp net 和 sql 服务器 我有 2 个表 类别和产品 在产品表中 我的categoryId 为FK 我想要做的是 当我从类别表中删除类别时 我希望该类别中的所有产品都将在产品表中删除 如何才能做到这一点 我更喜欢使用存
  • ClickOnce 应用程序错误:部署和应用程序没有匹配的安全区域

    我在 IE 中使用 FireFox 和 Chrome 的 ClickOnce 应用程序时遇到问题 它工作正常 异常的详细信息是 PLATFORM VERSION INFO Windows 6 1 7600 0 Win32NT Common
  • 在 iFrame 内维护会话状态

    不确定我是否疯了 但我在 iFrame 内的会话状态遇到问题 它是一个域在另一个域中的简单设置 我不需要跨域共享任何内容 我想做的就是将一个网站嵌入到另一个网站中 并且我希望该嵌入网站能够使用 cookie 会话状态登录 编辑 更新 等 为
  • 如何在没有@import的情况下减少@import?

    我用的较少 从 Google PageSpeed 我了解到 使用 importCSS 文件中的内容会影响网站速度 所以我想排除任何 import来自我的 CSS 的东西 我有 2 个不同的样式表reset css and rebuild c
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • ASP.NET Click() 事件在第二次回发时不会触发

    我有一个 ASP NET Web 表单 我第一次提交表单时 会引发 提交按钮单击 事件 表单返回到浏览器时可能会出现验证错误 或者可以选择使用新值再次提交表单 当再次提交表单时 提交按钮单击 事件永远不会触发 Page Load 触发 但按
  • 导致回发到与弹出窗口不同的页面

    我有一个主页和一个详细信息页面 详细信息页面是从主页调用的 JavaScript 弹出窗口 当单击详细信息页面上的 保存 按钮时 我希望主页 刷新 是否有一种方法可以调用主页的回发 同时还可以从详细信息页面维护保存回发 Edit 使用win

随机推荐