ASP.NET 组合框下拉列表带图片
一个ASP.NET组合框下拉列表,带图片,兼容 Internet Explorer/Firefox/Opera
引言
很难找到一个免费且兼容(所有浏览器)的下拉列表,可以包含图片和文本。然而,我看了 jQuery 的开源组件,发现 jQuery 开发者在他们开发的几个小部件上做得非常出色。jQuery 是一个强大的 JavaScript 框架,拥有非常漂亮且优化的组件。
我在一个简单的 HTML 页面中找到了他们下拉列表图片的示例,并决定尝试创建一个可扩展的 HTML 控件来封装这个下拉列表。简单示例的 URL 是 http://marghoobsuleman.com/jquery-image-dropdown。
Using the Code
基础组件是一个非常简单的基本 HtmlSelect
(System.Web.UI.Htmlcontrols
命名空间)。我扩展了这个类来添加特定的行为。
起初,我将组件的资源(JavaScript 文件、CSS 文件和我的扩展 HtmlSelect
类的箭头图片)放在了程序集中。我在重写的 PreRender
方法中添加了这些资源,并在 AssemblyInfo.cs 文件中添加了引用。微软的建议 在这里。所有这些资源都必须是“嵌入资源”。
以下代码向您展示了一个特殊情况:如何显示嵌入在程序集中的图片并在 CSS 代码中调用它。起初,HtmlSelect
的箭头图片在 dd.css 中这样引用
.dd .ddTitle span.arrow
{
float:right;
display:inline-block;
width:16px;
height:16px;
cursor:pointer;
background-image:url('arrow_d.gif');
}
ASP.NET 无法使用此声明显示 arrow_d.gif,因为图片的 URL 不是 arrow_d.gif。如果您想显示此图片,您必须遵循三个步骤
步骤 1:您必须使用 ClientScript
对象的 GetWebResourceUrl
方法获取图片 URL,如下所示
//get url of embedded image
string urlImageArrowCombo = Page.ClientScript.GetWebResourceUrl(this.GetType(),
"AssemblyName.dd_arrow.gif");
(在此示例中,图片位于程序集的根目录)。如果图片位于名为“toto”的程序集目录中,“AssemblyName.dd_arrow.gif”将变成“AssemblyName.toto.dd_arrow.gif”。
步骤 2:提取 CSS 文件中的 CSS 代码,并在服务器端注入图片 URL 到样式中
string urlImageArrowCombo =
Page.ClientScript.GetWebResourceUrl(this.GetType(), "ComboImg.dd_arrow.gif");
StringBuilder strStyleArrow = new StringBuilder();
strStyleArrow.Append(" .dd .ddTitle span.arrow { ")
.Append(" float:right; ")
.Append(" display:inline-block; ")
.Append(" width:16px; ")
.Append(" height:16px; ")
.Append(" cursor:pointer; ")
.Append(" background-image:url('" + urlImageArrowCombo + "');")
.Append(" } ");
HtmlGenericControl styleArrow = new HtmlGenericControl("style");
styleArrow.Attributes.Add("type", "text/css");
jqdd.Controls.Add(
new LiteralControl(strStyleArrow.ToString())
);
Page.Header.Controls.Add(styleArrow);
最后一步:如果在同一页面中有大量 HtmlSelect
图片,页面头部添加的样式必须被所有 HtmlSelect
图片控件共享。因此,测试 Page.Items.Contains("styleAlreadyPut"))
是必要的,以确保样式只添加一次。OnPreRender
事件用于添加资源,就像我刚才解释的那样。代码如下
/// Test if the Script manager is not missing
/// for the Sys.WebForms.PageRequestManager manipulation in prerender event
/// and load script and css
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (!this.DesignMode)
{
// Test for ScriptManager and register if it exists
// we need it for PageRequestManager manipulation in prerender
ScriptManager sm = ScriptManager.GetCurrent(Page);
if (sm == null)
throw new HttpException("A ScriptManager control " +
"must exist on the current page.");
//Add js and css files resource
string cssdd = Page.ClientScript.GetWebResourceUrl(GetType(),
"ComboImg.dd.css");
string scriptJQuery = Page.ClientScript.GetWebResourceUrl(this.GetType(),
"ComboImg.jquery-1.3.2.min.js");
string scriptJQuerydd = Page.ClientScript.GetWebResourceUrl(this.GetType(),
"ComboImg.jquery.dd.js");
HtmlLink lnk = new HtmlLink();
lnk.Href = cssdd;
lnk.Attributes["rel"] = "stylesheet";
lnk.Attributes["type"] = "text/css";
HtmlGenericControl jq = new HtmlGenericControl("script");
jq.Attributes.Add("language", "JavaScript");
jq.Attributes.Add("type", "text/javascript");
jq.Attributes.Add("src", scriptJQuery);
jq.Attributes.Add("alt", "scriptJQuery");
HtmlGenericControl jqdd = new HtmlGenericControl("script");
jqdd.Attributes.Add("language", "JavaScript");
jqdd.Attributes.Add("type", "text/javascript");
jqdd.Attributes.Add("src", scriptJQuerydd);
jqdd.Attributes.Add("alt", "scriptJQuerydd");
Page.Header.Controls.Add(jq);
Page.Header.Controls.Add(jqdd);
Page.Header.Controls.Add(lnk);
//Put one time the style whose gives reference to arrow of the combo
if (!Page.Items.Contains("styleAlreadyPut"))
{
//get url of embedded image
string urlImageArrowCombo = Page.ClientScript.GetWebResourceUrl
(this.GetType(), "ComboImg.dd_arrow.gif");
StringBuilder strStyleArrow = new StringBuilder();
strStyleArrow.Append(" .dd .ddTitle span.arrow { ")
.Append(" float:right; ")
.Append(" display:inline-block; ")
.Append(" width:16px; ")
.Append(" height:16px; ")
.Append(" cursor:pointer; ")
.Append(" background-image:url('" + urlImageArrowCombo + "');")
.Append(" } ");
HtmlGenericControl styleArrow = new HtmlGenericControl("style");
styleArrow.Attributes.Add("type", "text/css");
jqdd.Controls.Add(
new LiteralControl(strStyleArrow.ToString())
);
Page.Header.Controls.Add(styleArrow);
Page.Items.Add("styleAlreadyPut", "styleAlreadyPut");
}
}
}
我重写了 OnDataBinding
方法,因为基类的 OnDataBinding
仅在定义了 DataValueField
和 DataTextField
时执行特定处理。(使用 Reflector 查看 OnDataBinding
的源代码(它是你最好的朋友 :-)))。)
在我们的例子中,我们不使用这些属性,因为我们不需要它们,并且需要使用绑定的 DataTable
的值创建一个 ListItem
。最后一点要看的是 currentValue
属性,它允许在数据绑定之前保存当前选定的值,以便之后检索。
///Redefine OnDataBinding of the select
///without DataTextfiled and Datamember properties
protected override void OnDataBinding(EventArgs e)
{
IEnumerable data = base.GetData();
currentValue = Value;
if (data != null)
{
DataTable source = ((DataView)data).Table;
base.Items.Clear();
ICollection is2 = data as ICollection;
if (is2 != null)
{
this.Items.Capacity = is2.Count;
}
foreach (DataRow dr in source.Rows)
{
string value = FormatString(dr[0]);
string imgPath = FormatString(dr[1]);
string text = FormatString(dr[2]);
ListItem it = new ListItem();
it.Enabled = true;
it.Text = text;
it.Value = value;
it.Selected = (value.Equals(currentValue));
base.Items.Add(it);
}
}
Value = currentValue;
this.ViewState["_!DataBound"] = true;
this.RequiresDataBinding = false;
}
使用 Reflector,我查看了 HtmlSelect
类 RenderChildren
方法的实现。在我的情况下,我做了两个区分
- 无数据绑定:调用基类
RenderChildren
。 - 带数据绑定:重写
RenderChildren
实现以生成正确格式的选项标签。
正确格式是“项目文本”。
结论是,如果您想对此组合框进行数据绑定,您必须提供一个包含三列的 DataTable
- 项目值
- 图片路径
- 项目文本
这是 RenderChildren
实现
/// Generates options tags
protected override void RenderChildren(HtmlTextWriter writer)
{
if (DataSource != null)
{
if (DataSource.GetType() == typeof(DataTable))
{
DataTable source = (DataTable)DataSource;
if (source.Rows.Count == 0)
{
base.RenderChildren(writer);
return;
}
foreach (DataRow dr in source.Rows)
{
string value = FormatString(dr[0]);
string imgPath = FormatString(dr[1]);
string text = FormatString(dr[2]);
bool selected = currentValue.Equals(value);
if (selected)
writer.Write("" + text + "");
else
{
writer.Write("" + text + "");
}
writer.WriteLine();
}
}
}
else
{
base.RenderChildren(writer);
}
}
RenderControl
添加了一个脚本,该脚本允许使用强大的 jQuery 框架初始化下拉列表。要初始化下拉列表,您必须执行以下 jQuery 语句:$(document).ready(function() { $('#dropdownlistId').msDropDown(); }
。但是,ASP.NET 在加载阶段如果只执行这些语句会遇到 JavaScript 错误。
Arnold Matusz 是一位 jQuery ASP.NET 开发者,他使用 jQuery 与 ASP.NET 的解决方案 在这里。解决方案很简单:在 AJAX 请求事件结束时执行此脚本。
/// Set the jQuery script for the combo at the beginning of the page request
public override void RenderControl(HtmlTextWriter writer)
{
base.RenderControl(writer);
StringBuilder scriptInit = new StringBuilder();
scriptInit.Append(" <script type="\"text/javascript\""> ")
.Append(" Sys.WebForms.PageRequestManager.getInstance()" +
".add_endRequest(EndRequestHandler); ")
.Append(" function EndRequestHandler(sender, args) { ")
.Append(" if (args.get_error() == undefined) { ")
.Append(" setImageComBo_" + this.ID + "(); ")
.Append(" } ")
.Append(" } ")
.Append(" function setImageComBo_"+this.ID+"() { ")
.Append(" $(document).ready(function() { ")
.Append(" $('#"+this.ID +"').msDropDown(); ")
.Append(" }); ")
.Append(" } ")
.Append(" setImageComBo_" + this.ID + "(); ")
.Append("</script>");
writer.Write(scriptInit.ToString());
}
关注点
我学会了如何在 ASP.NET 中封装一个 jQuery 组件。我鼓励您尝试开发更多 jQuery ASP.NET Web 控件;它们非常有用且漂亮……
期待您的建议。
历史
- 2009年9月28日:初始帖子
- 2009年9月29日:更新源代码