如何缩短 ASP.NET 自动生成的控件 ID






4.44/5 (5投票s)
本文介绍如何通过更改 ASP.NET 生成控件 ID 的算法,甚至对于容器控件,来缩短 ASP.NET 自动生成的控件 ID。
引言
本文介绍如何通过更改 ASP.NET 生成控件 ID 的算法,甚至对于容器控件,来缩短 ASP.NET 自动生成的控件 ID。
背景
ASP.NET 生成的自动服务器控件 ID 可能非常长,并且可能会阻碍您的 Web 应用程序的 SEO 友好性。 这是因为搜索引擎蜘蛛程序在一定数量的字节后往往会停止分析您页面中的 HTML。 通常,ASP.NET 页面大小的一半以上可能归因于其服务器控件 ID 名称。
值得庆幸的是,Nuno Gomes 已经完成了他博客中的大部分工作。共有四个部分,位于此处和此处。 源代码可以在此处找到。
请花时间阅读他的帖子,然后再继续阅读本文,因为这将使您更容易理解我的文章的其余部分。 这里的主要改进是我将向您展示如何修改他的工作以与容器控件(例如ListView
、Repeater
和GridView
)一起使用。 这很重要,因为许多页面都有其中一个控件包含页面上的所有其他控件,因此如果您按原样使用 Nuno 的代码,则您的任何控件都不会缩短 ID。
实现
在 Nuno 的项目中,他基本上为他希望缩短 ID 的任何服务器控件创建了一个派生的自定义控件。 然后他重写了几个方法。 例如,我可以创建一个从 Button
派生的 NewButton
类
public class NewButton : System.Web.UI.WebControls.Button
{
#region Naming Management
/// <summary>
/// Gets or sets the programmatic identifier assigned to the server control.
/// </summary>
/// <value></value>
/// <returns>The programmatic identifier assigned to the control.</returns>
public override string ID
{
get { return NamingConfiguration.Provider.GetControlID(this, base.ID); }
set { base.ID = NamingConfiguration.Provider.SetControlID(value, this); }
}
/// <summary>
/// Creates a new <see cref="T:System.Web.UI.ControlCollection"></see>
/// object to hold the child controls
/// (both literal and server) of the server control.
/// </summary>
/// <returns>
/// A <see cref="T:System.Web.UI.ControlCollection"></see>
/// object to contain the current server control's
/// child server controls.
/// </returns>
protected override ControlCollection CreateControlCollection()
{
return NamingConfiguration.Provider.CreateControlCollection(this);
}
/// <summary>
/// Searches the current naming container for a server
/// control with the specified id and an integer,
/// specified in the pathOffset parameter, which aids
/// in the search. You should not override this version
/// of the FindControl method.
/// </summary>
/// <param name="id">The identifier for the control to be found.</param>
/// <param name="pathOffset">The number of controls
/// up the page control hierarchy needed to reach a
/// naming container.</param>
/// <returns>
/// The specified control, or null if the specified control does not exist.
/// </returns>
protected override Control FindControl(string id, int pathOffset)
{
Control ctrl = base.FindControl(id, pathOffset);
if (ctrl == null)
{
ctrl = NamingConfiguration.Provider.FindControl(this, id, pathOffset);
}
return ctrl;
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.Control.Init"></see> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"></see> object
/// that contains the event data.</param>
protected override void OnInit(EventArgs e)
{
this.EnsureID();
this.ID = base.ID;
base.OnInit(e);
}
#endregion Naming Management
}
上面的代码适用于任何非容器控件,但不适用于容器控件,我将其定义为任何实现 INamingContainer
的控件。 原因是因为像 Repeater
这样的控件将包含其中 RepeaterItem
类的一个集合。 您希望通过 web.config 文件中的 tagmapping
元素将 Repeater
的每个实例替换为您自己的 NewRepeater
实例,但您将无法通过标签映射将 RepeaterItem
实例替换为您自己的 NewRepeaterItem
,因为 RepeaterItem
中的代码专门只创建 RepeaterItem
实例。 简而言之,标签映射仅适用于在 *.aspx 或 *.ascx 文件中声明性定义的控件。
为了解决这个问题,我们需要在我们的 NewRepeater
类中重写另一个方法
protected override RepeaterItem CreateItem(int itemIndex, ListItemType itemType)
{
RepeaterItem rptrItem = base.CreateItem(itemIndex, itemType);
NewRepeaterItem newRptrItem = new NewRepeaterItem(itemIndex, itemType);
newRptrItem = Utilities.BaseToDerived(rptrItem, newRptrItem);
return newRptrItem;
}
您会在上面的代码中注意到,我调用了 Utilities.BaseToDerived
。 此方法基本上会执行反向转换,即,它会获取一个基类并返回它的一个派生版本。 派生版本将是我们 NewRepeaterItem
类的一个实例。 该方法的实现如下所示
public static T BaseToDerived<S, T>(S source, T target)
{
PropertyDescriptorCollection sourceproperties =
TypeDescriptor.GetProperties(source);
PropertyDescriptorCollection targetproperties =
TypeDescriptor.GetProperties(target);
foreach (PropertyDescriptor pd in targetproperties)
{
foreach (PropertyDescriptor _pd in sourceproperties)
{
if (pd.Name == _pd.Name)
{
pd.SetValue(target, _pd.GetValue(source));
}
}
}
return target;
}
您可以看到我们使用反射将所有匹配的属性从一个类复制到另一个类。 我承认,这实际上更多的是复制匹配的属性而不是转换,但它可以完成任务。 到目前为止,我在任何数据绑定事件中操纵派生类都没有遇到任何问题。
对于 Repeater 控件,您只需要担心其中的 RepeaterItem
对象。 其他容器控件(如 ListView
)可以包含 ListViewItem
对象和 ListViewDataItem
对象。 因此,您需要在 NewListView
类中重写多个 CreateXXX 方法。 这两个方法是 CreateItem
和 CreateDataItem
。
/// <summary>
/// Creates a data item in the
/// <see cref="T:System.Web.UI.WebControls.ListView"/> control.
/// </summary>
/// <param name="dataItemIndex">The index of the data item
/// in the underlying data source object.</param>
/// <param name="displayIndex">The display index of the data item
/// in the <see cref="T:System.Web.UI.WebControls.ListView"/> control.</param>
/// <returns>
/// A data item that is created by using the specified parameters.
/// </returns>
protected override ListViewDataItem CreateDataItem(int dataItemIndex, int displayIndex)
{
ListViewDataItem lvItem = base.CreateDataItem(dataItemIndex, displayIndex);
NewListViewDataItem newLvItem =
new NewListViewDataItem(dataItemIndex, displayIndex);
newLvItem = Utilities.BaseToDerived(lvItem, newLvItem);
return newLvItem;
}
/// <summary>
/// Creates a <see cref="T:System.Web.UI.WebControls.ListViewItem"/>
/// object with the specified type.
/// </summary>
/// <param name="itemType">One of the
/// <see cref="T:System.Web.UI.WebControls.ListViewItemType"/> values.</param>
/// <returns>
/// A <see cref="T:System.Web.UI.WebControls.ListViewItem"/>
/// object with the specified type.
/// </returns>
protected override ListViewItem CreateItem(ListViewItemType itemType)
{
ListViewItem lvItem = base.CreateItem(itemType);
NewListViewItem newLvItem = new NewListViewItem(itemType);
newLvItem = Utilities.BaseToDerived(lvItem, newLvItem);
return newLvItem;
}
请注意,使用 NewListView
需要显式设置 ItemPlaceholderID
属性。 我不确定为什么。
结论
缩短 ASP.NET 服务器控件的自动生成的 ID 名称可以显着减少您的页面大小并提高 SEO。 在本文中,我向您展示了如何通过使用 Nuno Gomes 博客条目中的代码来做到这一点,然后修改了他的代码,使其可以与容器控件一起使用。 祝您编码愉快!
历史
- 2009 年 3 月 13 日:初始帖子
- 2009 年 3 月 16 日:文章更新