ASP.NET 2.0的ScrollableListBox自定义控件

分享于 

18分钟阅读

Web开发

  繁體

ScrollableListbox

介绍

我一直在寻找一个 List 框控件,它支持水平滚动和完全像 ASP.NET ListBox 一样的行为。 在lintom文章中发现了 ,这使得我们不必提供重用功能就可以单独处理每个 ListBox。 我还找到了 XList控件,它是一个可以重用控件,但它与普通 ASP.NET ListBox 控件的行为不完全一样。 一段时间之后,我决定开发自己的控制。 本文将介绍使用 C# 和 ASP.NET 2.0技术创建的ScrollableListBox 定制控件。 来自 ListBoxScrollableListBox 支持水平滚动条,但它的行为类似于 ASP.NET ListBox 控件。

[ToolboxBitmap(typeof(Evyatar.Web.Controls.ScrollableListBox), 
 "Evyatar.Web.Controls.ScrollableListBox.bmp")]publicclass ScrollableListBox : ListBox
{
. . .
}

添加水平滚动条

behind 添加水平滚动条的主要思想是将 <选择> HTML元素( 表示 ListBox ) <DIV> HTML元素并让 <DIV> 处理滚动条的步骤。 这种方法使我们将控件的Height 属性和 Width 属性放在 <DIV> 要保持它像 ASP.NET ListBox 一样,我们需要向下钻取并询问浏览器如何定义 ListBox的测量。 <选择> HTML元素在 IE的情况下,这个问题的答案是:? 宽度由最长的ListItem 或者 <选项> HTML元素,除非有显式的宽度定义。 height由呈现到对象的size 属性的ListBoxRows 属性定义 <选择> HTML元素,除非存在高度的显式定义。

为了消除 HeightWidth 属性, <选择> HTML元素,我们应该重写 HeightWidth 属性,如下所示:

publicoverride Unit Width
{
 get {
 object o = ViewState["Width"];
 if (null == o)
 return Unit.Empty;
 return (Unit)o;
 }
 set {
 if (value.Value <0)
 {
 thrownew ArgumentOutOfRangeException("value");
 }
 ViewState["Width"] = value;
 }
}publicoverride Unit Height
{
 get {
 object o = ViewState["Height"];
 if (null == o)
 return Unit.Empty;
 return (Unit)o;
 }
 set {
 if (value.Value <0)
 {
 thrownew ArgumentOutOfRangeException("value");
 }
 ViewState["Height"] = value;
 }
}

为了包装 <选择> HTML元素 <DIV> HTML元素,应重写 Render 方法,如下所示:

protectedoverridevoid Render(HtmlTextWriter writer)
{
 writer.Write(string.Format("<div style='OVERFLOW-X:" + 
 " auto;OVERFLOW-Y: auto; {0}' id='{1}'>", 
 AddStyleAttributesToRender(), ClientID + "_div"));
 base.Render(writer);
 writer.Write("</div>");
}privatestring AddStyleAttributesToRender()
{
 StringBuilder sb = new StringBuilder("", 16);
 if (!Width.IsEmpty)
 sb.Append(string.Format("WIDTH: {0};", 
 Width.ToString(CultureInfo.InvariantCulture)));
 if (!Height.IsEmpty)
 sb.Append(string.Format("HEIGHT: {0};", 
 Height.ToString(CultureInfo.InvariantCulture)));
 return sb.ToString();
}

Render 方法首先呈现的开始标记 <DIV> HTML元素,指示它的高度和宽度 <DIV> 它的水平和垂直滚动条的行为当 <DIV> 内容溢出。然后,我们渲染 <选择> 通过调用 ListBox 控件(。哪个是基类)的Render 方法来调用HTML元素。 这里,我们应该知道方法的ListBoxRender 将不会呈现 HeightWidth 属性。 当我们将值设置为 Height 或者 Width 属性时,我们不会将这里值传递给基类。 finally,我们渲染的结束标记 <DIV> HTML元素。

我们即将完成,但仍然需要处理控件度量的浏览器定义:

方案当前行为预期行为
控件 Width 没有显式定义。iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <选择> 宽度由最长 <选项> 元素,但是, <DIV> 宽度被确定为 100% (。IE的默认行为)。iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <DIV> 宽度应确定为 <选择> HTML元素。
控件 Width 有明确的定义。iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <选择> 宽度由最长 <选项> 元素。 <DIV> 根据控件 Width 定义确定宽度。 以防最长 <选项> 元素比控件 Width的显式定义宽,水平滚动条将出现。以防最长 <选项> 元素的宽度大于 <DIV> 元素,则当前行为也是预期的行为。 否则 <选择> 元素应拉伸以填充 <DIV> 区域。
控件 Height 没有显式定义。iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <选择> 高度由控件的Rows 属性决定。 iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <DIV> 高度由它的内容决定。 如果 Rows 属性小于 List 项的数量,则垂直滚动条将出现在 <选择> HTML元素。如果 List 项的数量小于 Rows 定义,则不应该出现垂直滚动条。 否则,垂直滚动条应出现在 <DIV> 元素而不是 <选择> 元素。可见项的数量应根据 Rows 属性,即使有水平滚动条( 可能出现和消费区域)。
控件 Height 有明确的定义。iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <选择> 高度由控件的Rows 属性决定。 iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <DIV> 高度由控件的Height 属性决定。 如果 Rows 属性小于 List 项的数量,则垂直滚动条将出现在 <选择> HTML元素。如果 <DIV> 高度超出了 <选择> 高度之间有一个间隙 <DIV> 它的内容。iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 <选择> 元素不应包含垂直滚动条。 此外,不应该在 <DIV> 元素及其内容。

为了解决 上面 问题,我们需要知道客户端的客户端宽度和客户端的高度 <选择> HTML元素。该信息仅在呈现控件后在客户端中可用。 因此,为了解决这个问题,我们将注入一个JavaScript代码,该代码在控件呈现之后被调用。

首先,让我们看一下将调用的JavaScript代码。 iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 __ScrollableListBoxRefineHeightAndWidth 函数测试 上面 场景,并确保该控件的行为与预期的一样。

function __ScrollableListBoxRefineHeightAndWidth(list_id)
{
 var list = document.getElementById(list_id);
 var div = document.getElementById(list_id + '_div');
 if (div.style.height) 
 {
 // div has height we will expand the list to fit the div's height.// In addition we will make sure the vertical// scroll bar of the list will not// displayed list.size = list.options.length;
 while (list.clientHeight <div.clientHeight)
 list.size = list.size + 1;
 }
 if (div.style.width)
 { 
 if (div.clientWidth> list.clientWidth)
 {
 // div is wider than list. We will// wide the list to fill the div area list.style.width = div.clientWidth;
 }
 if (!div.style.height)
 {
 // div have no height this is mean the height is defined by list rows.// We will set the div height to be like the list height. div.style.height = list.offsetHeight + 
 (list.offsetHeight - div.clientHeight);
 // now after we set the div height,// we need to remove the vertical scroll bar of the list // (it will exists when the size of the list is less than the number // the amount of list's items).// We are doing it by set the list rows if (list.size <list.options.length)
 {
 list.size = list.options.length;
 }
 else {
 // here we will not need the vertical// scroll bar. therefore we will hide it. div.style.overflowY = 'hidden';
 }
 }
 }
 else 
 {
 // div has no width. This is mean the width will be set // by the longest list's item. Therefore we will// set the div width like the list offset. div.style.width = list.offsetWidth;
 // here we will not need the horizontal scroll bar.// therefor we will hide it. div.style.overflowX = 'hidden';
 }
}

现在,我们应该将 上面 代码注入客户端浏览器,并将调用注入到 __ScrollableListBoxRefineHeightAndWidth 控件呈现后的函数。 为了在服务器端代码和客户端代码之间保持清晰的分离,我将 __ScrollableListBoxRefineHeightAndWidth 在单独的文件 ScrollableListBox.js. 中,文件被编译为我的控制库项目中的嵌入资源。 现在,我们所需要做的就是将包含脚本 register的ScrollableListBox.js 文件和 register 调用到的启动脚本。 __ScrollableListBoxRefineHeightAndWidth 函数:

protectedoverridevoid OnInit(EventArgs e)
{
 ClientScriptManager cs = Page.ClientScript;
 Type rsType = this.GetType();
 if (!cs.IsClientScriptIncludeRegistered("ScrollableListBox"))
 {
 cs.RegisterClientScriptInclude("ScrollableListBox", 
 cs.GetWebResourceUrl(rsType, 
 "Evyatar.Web.Controls.JavaScript.ScrollableListBox.js"));
 }
 cs.RegisterStartupScript(rsType, "ScrollableListBoxStartup" + this.UniqueID,
 string.Format("__ScrollableListBoxRefineHeightAndWidth('{0}');", 
 this.ClientID), true);
 base.OnInit(e);
}

外观特性的复苏

当我检查 ListBox 和它在 IE 上呈现时,我注意到 BorderColorBorderStyleBorderWidth 属性对 ListBox 控件的外观没有影响。 样式 属性 <选择> HTML元素。这就是设计时不支持这些属性的原因。

由于 ScrollableListBox 被呈现到 <选择> HTML元素被 <DIV> HTML元素中,我们希望在 样式 属性 <DIV> HTML元素。另外,我们希望为这些属性添加设计时支持。

[Browsable(true)][Category("Appearance")]
[Description("Color of the border around the control"), 
 DefaultValue(typeof(Color),"")]
[TypeConverter(typeof(WebColorConverter))]publicoverride Color BorderColor
{
 get {
 object o = ViewState["BorderColor"];
 if (null == o)
 return Color.Empty;
 return (Color)o;
 }
 set {
 ViewState["BorderColor"] = value;
 }
}
[Browsable(true)]
[DefaultValue(BorderStyle.NotSet)]
[Category("Appearance")]
[Description("Style of the border around the control")]publicoverride BorderStyle BorderStyle
{
 get {
 object o = ViewState["BorderStyle"];
 if (null == o)
 return System.Web.UI.WebControls.BorderStyle.NotSet;
 return (BorderStyle)o;
 }
 set {
 ViewState["BorderStyle"] = value;
 }
}
[Browsable(true)]
[Description("Width of the border around the control")]
[Category("Appearance")]
[DefaultValue(typeof(Unit), "")]publicoverride Unit BorderWidth
{
 get {
 object o = ViewState["BorderWidth"];
 if (null == o)
 return Unit.Empty;
 return (Unit)o;
 }
 set {
 if (value.Value <0)
 {
 thrownew ArgumentOutOfRangeException("value");
 }
 ViewState["BorderWidth"] = value;
 }
}

现在,为了支持外观属性,我们将修复 AddStyleAttributesToRender 方法:

privatestring AddStyleAttributesToRender()
{
 StringBuilder sb = new StringBuilder("", 16);
 if (!Width.IsEmpty)
 sb.Append(string.Format("WIDTH: {0};", 
 Width.ToString(CultureInfo.InvariantCulture)));
 if (!Height.IsEmpty)
 sb.Append(string.Format("HEIGHT: {0};", 
 Height.ToString(CultureInfo.InvariantCulture)));
 if (! BorderColor.IsEmpty) 
 sb.Append(string.Format("BORDER-COLOR: {0};", 
 ColorTranslator.ToHtml(BorderColor)));
 if (!BorderWidth.IsEmpty)
 sb.Append(string.Format("BORDER-WIDTH: {0};", 
 BorderWidth.ToString(CultureInfo.InvariantCulture)));
 if (BorderStyle!= BorderStyle.NotSet)
 sb.Append(string.Format("BORDER-STYLE: {0};", BorderStyle.ToString()));
 return sb.ToString();
}

使用代码

如果你是 ASP.NET 程序员,你应该熟悉 ListBox 控件。 你可能会高兴地看到 ScrollableListBox 控件是由 ListBox 控件派生的。 因此,ListBox的所有功能( 如数据绑定。ViewState。事件通知。等等 ) 都将保留在使用 ScrollableListBox 控件时的状态。 使用 ScrollableListBox 控件所需做的就是将它添加到工具箱中并将它的拖到页面中。 从现在开始,你可以使用与使用 ListBox 控件相同的方式。

关于演示项目

演示项目允许你检查和比较 ListBox 控件和 ScrolllableListBox 控件的行为。 运行项目,并使用演示项目页定义控件属性,例如 HeightWidthRowsBorderColorBorderStyleBorderWidth。 这里外,当你定义显式 Height 时,尝试将长项目添加到列表中,并且当你没有执行这里操作时。

摘要

来自 ListBoxScrollableListBox 支持水平滚动条,但它的行为类似于 ASP.NET ListBox。 此外,它还提供了 ListBox 控件不支持的一些外观特性。


相关文章