选择了ASP.NET的开发人员一直都很郁闷,因为选择了ASP.NET就是选择了它已有的强大的Web控件库,然后选择了这些控件的强大功能的同时也带了一些问题,比如现在的有些复杂控件,它输出的HTML并不符合现在的WEB标准,例如GridView,TreeView,用户控件等等这些控件输出的HTML都是传统的Table的方式,并不像WEB标准倡导的用控制显示方式的CSS和内容Div分离的方式!
ASP.NET开发团队也意识到这个这问题,借助于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输出!