65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 组合框下拉列表带图片

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (16投票s)

2009 年 9 月 28 日

CPOL

4分钟阅读

viewsIcon

222951

downloadIcon

7415

一个ASP.NET组合框下拉列表,带图片,兼容 Internet Explorer/Firefox/Opera

静态模式 DropdownlistImage

drop.JPG

动态模式 DropdownlistImage,带动态数据绑定

drop.JPG

引言

很难找到一个免费且兼容(所有浏览器)的下拉列表,可以包含图片和文本。然而,我看了 jQuery 的开源组件,发现 jQuery 开发者在他们开发的几个小部件上做得非常出色。jQuery 是一个强大的 JavaScript 框架,拥有非常漂亮且优化的组件。

我在一个简单的 HTML 页面中找到了他们下拉列表图片的示例,并决定尝试创建一个可扩展的 HTML 控件来封装这个下拉列表。简单示例的 URL 是 http://marghoobsuleman.com/jquery-image-dropdown

Using the Code

基础组件是一个非常简单的基本 HtmlSelectSystem.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 仅在定义了 DataValueFieldDataTextField 时执行特定处理。(使用 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,我查看了 HtmlSelectRenderChildren 方法的实现。在我的情况下,我做了两个区分

  • 无数据绑定:调用基类 RenderChildren
  • 带数据绑定:重写 RenderChildren 实现以生成正确格式的选项标签。

正确格式是“项目文本”

结论是,如果您想对此组合框进行数据绑定,您必须提供一个包含三列的 DataTable

  1. 项目值
  2. 图片路径
  3. 项目文本

这是 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日:更新源代码
© . All rights reserved.