選擇了ASP.NET的開發人員一直都很鬱悶,因為選擇了ASP.NET就是選擇了它已有的強大的Web控制項陳列庫,然後選擇了這些控制項的強大功能的同時也帶了一些問題,比如現在的有些複雜控制項,它輸出的HTML並不符合現在的WEB標準,例如GridView,TreeView,使用者控制項等等這些控制項輸出的HTML都是傳統的Table的方式,並不像WEB標準倡導的用控制顯示方式的CSS和內容Div分離的方式!
ASP.NETTeam Dev也意識到這個這問題,藉助於ASP.NET 2.0架構強大的可配置、可自訂能力,給出瞭解決問題的完美方案——使用.blowser檔案為頁面中的控制項配置自訂的Adapter,來替代原有的非標準的解決方案。這些自訂的Adapter的集合就是ASP.NET 2.0 CSS Friendly Control Adapters。在發布了若干個測試版本之後,ASP.NET 2.0 CSS Friendly Control Adapters 的1.0版本終於正式發布。
ASP.NET 2.0中這些不符合Web標準的控制項如下,每一種都在ASP.NET 2.0 CSS Friendly Control Adapters中提供了符合Web標準的替代實現。
- Menu
- TreeView
- DetailsView
- FormView
- GridView
- DataList
- Login
- ChangePassword
- PasswordRecovery
- CreateUserWizard
- LoginStatus
在App_Browsers檔案夾下,可以看到CSSFriendlyAdapters.browser檔案,我們來看看它是如何配置的
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.WebControls.Menu"
adapterType="CSSFriendly.MenuAdapter" />
<adapter controlType="System.Web.UI.WebControls.TreeView"
adapterType="CSSFriendly.TreeViewAdapter" />
<adapter controlType="System.Web.UI.WebControls.DetailsView"
adapterType="CSSFriendly.DetailsViewAdapter" />
<adapter controlType="System.Web.UI.WebControls.FormView"
adapterType="CSSFriendly.FormViewAdapter" />
<adapter controlType="System.Web.UI.WebControls.DataList"
adapterType="CSSFriendly.DataListAdapter" />
<adapter controlType="System.Web.UI.WebControls.GridView"
adapterType="CSSFriendly.GridViewAdapter" />
<adapter controlType="System.Web.UI.WebControls.ChangePassword"
adapterType="CSSFriendly.ChangePasswordAdapter" />
<adapter controlType="System.Web.UI.WebControls.Login"
adapterType="CSSFriendly.LoginAdapter" />
<adapter controlType="System.Web.UI.WebControls.LoginStatus"
adapterType="CSSFriendly.LoginStatusAdapter" />
<adapter controlType="System.Web.UI.WebControls.CreateUserWizard"
adapterType="CSSFriendly.CreateUserWizardAdapter" />
<adapter controlType="System.Web.UI.WebControls.PasswordRecovery"
adapterType="CSSFriendly.PasswordRecoveryAdapter" />
</controlAdapters>
</browser>
<browser parentID="default">
<identification>
<userAgent match="^W3C_Validator" />
</identification>
<capabilities>
<capability value="W3C Validator" />
<capability value="1.2" />
<capability value="true" />
<capability value="true" />
<capability value="true" />
<capability value="true" />
<capability value="System.Web.UI.HtmlTextWriter" />
<capability value="1.0" />
</capabilities>
</browser>
</browsers>
我們可以看到這些像System.Web.UI.WebControls.Menu一樣的Web控制項都配置了adapterType屬性CSSFriendly.MenuAdapter
下面我們再來看看CSSFriendly.MenuAdapter裡面的一些代碼
private void BuildItems(MenuItemCollection items, bool isRoot, HtmlTextWriter writer)
{
if (items.Count > 0)
{
writer.WriteLine();
writer.WriteBeginTag("ul");
if (isRoot)
{
writer.WriteAttribute("class", "AspNet-Menu");
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
foreach (MenuItem item in items)
{
BuildItem(item, writer);
}
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("ul");
}
}
private void BuildItem(MenuItem item, HtmlTextWriter writer)
{
Menu menu = Control as Menu;
if ((menu != null) && (item != null) && (writer != null))
{
writer.WriteLine();
writer.WriteBeginTag("li");
string theClass = (item.ChildItems.Count > 0) ? "AspNet-Menu-WithChildren" : "AspNet-Menu-Leaf";
string selectedStatusClass = GetSelectStatusClass(item);
if (!String.IsNullOrEmpty(selectedStatusClass))
{
theClass += " " + selectedStatusClass;
}
writer.WriteAttribute("class", theClass);
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
if (((item.Depth < menu.StaticDisplayLevels) && (menu.StaticItemTemplate != null)) ||
((item.Depth >= menu.StaticDisplayLevels) && (menu.DynamicItemTemplate != null)))
{
writer.WriteBeginTag("div");
writer.WriteAttribute("class", GetItemClass(menu, item));
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
MenuItemTemplateContainer container = new MenuItemTemplateContainer(menu.Items.IndexOf(item), item);
if ((item.Depth < menu.StaticDisplayLevels) && (menu.StaticItemTemplate != null))
{
menu.StaticItemTemplate.InstantiateIn(container);
}
else
{
menu.DynamicItemTemplate.InstantiateIn(container);
}
container.DataBind();
container.RenderControl(writer);
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("div");
}
else
{
if (IsLink(item))
{
writer.WriteBeginTag("a");
if (!String.IsNullOrEmpty(item.NavigateUrl))
{
writer.WriteAttribute("href", Page.Server.HtmlEncode(menu.ResolveClientUrl(item.NavigateUrl)));
}
else
{
writer.WriteAttribute("href", Page.ClientScript.GetPostBackClientHyperlink(menu, "b" + item.ValuePath.Replace(menu.PathSeparator.ToString(), "\\"), true));
}
writer.WriteAttribute("class", GetItemClass(menu, item));
WebControlAdapterExtender.WriteTargetAttribute(writer, item.Target);
if (!String.IsNullOrEmpty(item.ToolTip))
{
writer.WriteAttribute("title", item.ToolTip);
}
else if (!String.IsNullOrEmpty(menu.ToolTip))
{
writer.WriteAttribute("title", menu.ToolTip);
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
}
else
{
writer.WriteBeginTag("span");
writer.WriteAttribute("class", GetItemClass(menu, item));
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
}
if (!String.IsNullOrEmpty(item.ImageUrl))
{
writer.WriteBeginTag("img");
writer.WriteAttribute("src", menu.ResolveClientUrl(item.ImageUrl));
writer.WriteAttribute("alt", !String.IsNullOrEmpty(item.ToolTip) ? item.ToolTip : (!String.IsNullOrEmpty(menu.ToolTip) ? menu.ToolTip : item.Text));
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
}
writer.Write(item.Text);
if (IsLink(item))
{
writer.Indent--;
writer.WriteEndTag("a");
}
else
{
writer.Indent--;
writer.WriteEndTag("span");
}
}
if ((item.ChildItems != null) && (item.ChildItems.Count > 0))
{
BuildItems(item.ChildItems, false, writer);
}
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("li");
}
}
可以看出來,通過設定檔.Browser檔案配置每個控制項所用到的System.Web.UI.WebControls.Adapters,使用這些自訂的Adapters來改變控制項的HTML輸出!