更新:これは、更新されたリンクとコードを含むブログ投稿になりました。 asp-net-themes-with-the-styleplaceholder-and-style-control/
問題はとても単純です。ASP.NET テーマを使用する場合、スタイル シートがページにどのようにレンダリングされるかについて、あまり発言権がありません。
レンダリング エンジンは、<link href="..." 表記を使用して、テーマ フォルダーにあるすべてのスタイル シートをアルファベット順に追加します。
スタイル シートの順序が重要であることは誰もが知っています。幸いなことに、スタイル シートの前に 01、02、...、99 を付けて、必要な順序を強制することで、asp.net の欠点を回避できます (Rusty Swayne のブログ投稿を参照してください)。詳細については、テクニックを参照してください)。
これは、リセット スタイル シートを使用する場合に特に重要です。これにより、ブラウザー間で一貫した形式でサイトのスタイルを設定することがはるかに簡単になります ( Eric Meyer の Reset Reloaded をご覧ください)。
また、メディアの種類 (スクリーン、印刷物、投影、点字、スピーチなど) を指定する可能性もありません。また、@import メソッドを使用してスタイル シートをインクルードしたい場合も、取り残されてしまいます。
不足しているもう 1 つのオプションは条件付きコメントです。これは、「ie-fix.css」スタイル シートを使用する場合に特に役立ちます。
StylePlaceholder と Style コントロールが上記の問題をどのように解決するかを説明する前に、私の解決策はPer Zimmerman のこの件に関するブログ投稿に触発されたものです。
StylePlaceHolder コントロールは、マスター ページまたはページのヘッダー セクションに配置されます。1 つまたは複数のスタイル コントロールをホストでき、デフォルトでレンダリング エンジンによって追加されたスタイルを削除し、独自のスタイルを追加します (現在アクティブなテーマから追加されたスタイルのみを削除します)。
Style コントロールは、開始タグと終了タグの間のインライン スタイルと、その CssUrl プロパティを介した外部スタイル シート ファイルへの参照の両方をホストできます。他のプロパティを使用して、スタイル シートをページにレンダリングする方法を制御します。
例を示しましょう。マスター ページと 3 つのスタイル シート (01reset.css、02style.css、99iefix.cs) を含むテーマを持つ単純な Web サイト プロジェクトを考えてみましょう。注: 設計時のエクスペリエンスが向上するため、前述のプレフィックス手法を使用して名前を付けました。また、カスタム コントロールのタグ プレフィックスは「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>
それは基本的にそれです。スタイル コントロールには、レンダリングを制御するために使用できるプロパティが他にもありますが、これは基本的な設定です。これがあれば、別のスキン ファイルを含めるだけでよいので、簡単に別のテーマを追加してすべてのスタイルを置き換えることができます。
次に、すべてを実現するコードに移ります。設計時の経験にはいくつかの癖があることを認めなければなりません。これはおそらく、私がカスタム コントロールの作成にあまり習熟していないためです (実際、これらの 2 つは初めての試みです)。そのため、次の点について意見をいただければ幸いです。現在開発中の 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 ソリューションの圧縮バージョンをアップロードしました。
よろしく、エギル。