组合框控件

分享于 

16分钟阅读

Web开发

  繁體

介绍

我总是想知道为什么微软,或者它的他的问题,从来没有提供一个本机 HTML ComboBox 元素。 在网上搜索一些东西后,我决定把事情归入我自己的手中。 我想要一个与默认HTML有视觉区别的控件 选择 元素并提供自动完成和 List 验证等功能。 本文将详细讨论用JavaScript编写的视图链接行为和组成控件的C# 代码。

历史记录

这里网页控件的版本 1.0基于由 Jeremy,。 jeremy的行为很好,但它并没有把我想要的所有特性都包含在组合框中。 这里更新是对该行为的全部重写,并解决了以下功能请求和 Bug 修复。

  • ListItem 值检索- 组合框值现在映射到 ListItem ( 选项 ) 值属性。
  • 自动验证- 现在可以指定combobox以验证用户根据 ListItem 文档中的文本输入的文本。
  • 文本字段 OnChange 事件- 将字段事件的文本 OnChange 冒泡到combobox控件。
  • z 索引- 下拉箭头将不再超过其他HTML元素。
  • 设计器模式下的数据绑定支持。
  • HTML的视觉区别 选择 元素。
  • 定位( Bug 修复) - 组合框现在在打印或者绝对定位时显示正确。
  • 第一项选择( Bug 修复) - 你现在可以选择下拉列表中的第一项。

使用组合框控件

注:这里控件是针对 IE 6.0开发和测试的。 但是可以能是 IE 5.5兼容,但不能与旧版本或者任何非,浏览器兼容。

对于所有的新手,仔细阅读并遵循说明 ! 我不会答复本部分中回答的任何查询。

首先,我们必须复制运行时文件,由 combobox.htc 和映像目录组成,你可以通过将新元素添加到集合中的appSettings 集合来指定其他位置。

<appSettings><addkey="PROGWEBCONTORLS_COMMONFILEPATH"value="MyVirtualPath"/></appSettings>

源文件中包含的二进制文件和项目文件基于. NET 框架 1.1和 Visual Studio 2003。 它们与 1.0或者 VS.NET 2002不兼容,因此你必须在自己的程序集上重新编译程序集。 要在 VS.NET 2002中重新编译项目,请按照以下步骤操作:

  • 创建新的C#"web控件库"项目;将它的命名为"。progstudios。坐标控制器"或者任何你想要的。
  • 右键单击项目并选择属性。
  • 确保"程序集 NAME"和"默认命名空间"属性设置为"。progstudios。坐标控制器"。
  • 将文件从ZIP文件拖放到项目上。
  • 右键单击位图图像,然后在属性窗口中将"生成操作"属性设置为"嵌入式资源"。
  • 右键单击解决方案并选择属性。 在"控件属性/配置"下,确保配置设置为"释放",然后按确定。
  • 右键单击项目并选择"生成",或者按"ctrl+shift+b"生成项目。
  • 输出DLL将位于 bin/release目录中。
  • 将控件添加到工具箱中。
  • 右键单击要放置控件的选项卡,然后选择"添加/删除项目"。
  • 单击"浏览"并找到 DLL。
  • 瞧!

要将程序集添加到web项目,右键单击解决方案浏览器中的"引用"并通过单击"浏览"找到 DLL,或者在"项目"选项卡中添加项目。 如果没有使用 Visual Studio,请确保将DLL复制到bin目录。 一旦安装了控件,可以将控件拖放到设计模式下的web页面。 下面是生成的HTML代码。

第一行注册程序集并将" cc1"标记前缀与程序集命名空间关联。 " cc1:combobox"代表实际控件。 子元素 asp:ListItem 可以与传统元素交换 <选项> 标签。

<%@RegisterTagPrefix="cc1"Namespace="ProgStudios.WebControls"Assembly="ProgStudios.WebControls"%><html><body><cc1:comboboxid="ComboBox1"runat="server"><asp:ListItem>ComboBox</asp:ListItem></cc1:combobox></body></html>

你可以手动添加新项目,或者像在演示项目中一样将它的绑定到 DataViewHtmlSelectDropDown 控件一样,ComboBox 可以绑定到任何实现 IListSourceIList 或者 IEnumerable的实例。

<cc1:ComboBoxid="ComboBox1"runat="server"DataSource="<%# dataView1 %>"DataValueField="ProductID"DataTextField="ProductName">. . .
 protected DataView dataView1;
 protected void Page_Load(object sender, System.EventArgs e) {
 if (!this.IsPostBack) {
 DataSet ds = new DataSet();
 FileStream fs = new FileStream(
 Server.MapPath("newdataset.xml"),
 FileMode.Open,FileAccess.Read);
 StreamReader reader = new StreamReader(fs);
 ds.ReadXml(reader);
 fs.Close();
 dataView1 = new DataView(ds.Tables[0]);
 ComboBox1.DataBind();
 }
 }
...<prog:comboboxid="ComboBox1"runat="server"/>

控件还公开服务器端事件,ServerChange。 你可以通过钩子 OnServerChange 事件处理程序来附加到事件。 如果在回发时更改了值,则会。 你还可以通过设置 AutoPostBack 属性来指定要自动回发的控件 true

<scriptrunat="server"language="c#">void ComboBox1_OnServerChange(object sender, System.EventArgs e) {
 Msg.InnerText = "ServerChange Event Fired: Value=" + ComboBox1.Value;
 }</script>....<prog:comboboxid="ComboBox1"runat="server"AutoPostBack="true"OnServerChange="ComboBox1_OnServerChange">

AutoValidate 属性用于确保包含在 List 项 Collection 中的用户 MATCHES 所键入的文本。 如果不 MATCH,则 selectedIndex 设置为 -1,并将该值设置为空字符串。 当 ComboBox 失去焦点时,可以通过设置 ErrorMessage 属性来通知错误消息。

在 C# Web控件代码内部

为了获得一些免费的功能,我决定从 WebControl 类中进行 inherit。 这个决定很容易,因为基类有一个 AttributesCollection 类成员。 这很重要,因为所有HTML元素都支持nmake属性,这基本上是任意属性,可以在客户端脚本中使用。 服务器控件无法识别的任何属性将附加到属性 Collection,并呈现到客户端上的root 元素。

剩下的代码很简单。 除了公开web客户端可以用的相应服务器端属性。方法和事件之外,我还必须实现一些管道。 控件必须参与回发以处理状态,因此, IPostBackDataHandler.RaisePostDataChangedEvent 并实现 IPostBackDataHandler.LoadPostData 接口方法来加载已经发布的值。

...publicvirtualvoid RaisePostDataChangedEvent() {
 this.OnServerChange(EventArgs.Empty);
}publicvirtualbool LoadPostData(string postDataKey, 
 NameValueCollection postCollection) {
 string sValue = this.Value;
 string sPostedValue = postCollection.GetValues(postDataKey)[0];
 if (!sValue.Equals(sPostedValue)) {
 this.Value = sPostedValue;
 returntrue; 
 }
 returnfalse;
}
...

我还必须重写 SaveViewStateLoadViewState 方法,以便管理 ListItem 后端的。

...protectedoverridevoid LoadViewState(object savedState) {
 if (savedState!= null) {
 object[] State = (object[])savedState;
 this.Value = (string) State[0];
 ArrayList ItemsList = (ArrayList) State[1];
 foreach (object itemText in ItemsList) {
 if (this.Items.FindByText((string)itemText)==null) {
 ListItem item = new ListItem();
 item.Text = (string) itemText;
 this.Items.Add(item);
 }
 }
 }
}protectedoverrideobject SaveViewState() {
 ArrayList ItemsList = new ArrayList();
 foreach (ListItem item inthis.Items) {
 ItemsList.Add(item.Text);
 }
 object[] savedState = new object[2];
 savedState[0] = this.Value;
 savedState[1] = ItemsList;
 return savedState;
}
...

编写控件生成器类也是一个非常简单的任务。 我继承了 ControlBuilder 并重写了 GetChildControlType 方法,只允许 ListItem选项 子元素。

...publicoverride Type GetChildControlType(string tagName, 
 System.Collections.IDictionary attribs) {
 string szTagName = tagName.ToLower();
 int colon = szTagName.IndexOf(':');
 if ((colon >= 0) && (colon < (szTagName.Length + 1))) {
 // Separate the tagname from the namespace szTagName = szTagName.Substring(colon + 1, 
 szTagName.Length - colon - 1);
 }
 if (String.Compare(szTagName, "option", true, 
 System.Globalization.CultureInfo.InvariantCulture) == 0 ||
 String.Compare(szTagName, "listitem", true, 
 System.Globalization.CultureInfo.InvariantCulture) == 0 ) {
 returntypeof(System.Web.UI.WebControls.ListItem); 
 }
 // No Type was found, throw an exceptionthrownew Exception(String.Format(
 "Invalid child with tagname"{0}"", tagName));
}

组合框链接视图行为

现在,我们进入文章中最有趣的部分。 本节将在某些程度上隐幻我的朋友称为web开发的black 艺术,行为编程。 当前版本 ComboBox 使用在元素行为中找到的ViewLink特性,它允许你在HTML组件中封装文档 Fragments。 作为主元素的ViewLink元素行为也维护它自己的文档树。 这样,我们现在可以使用HTML创建元素和样式表属性,并通过我们的HTC文件中的脚本操作这些对象,而不用担心名字冲突。

例如 ComboBox 定义了一个文本 输入 ID为" textField"的字段。 如果使用传统元素行为,则必须使用 document.createElement 方法定义元素,然后使用 element.appendChild 方法将它的添加到主文档树。 但是,如果使用视图链接特性,就可以使用HTML创建和定义元素。 这里外,如果我们希望在页面上使用多个 ComboBox 或者定义一个完全不同的元素的另一个元素行为。 我们会遇到 NAME 碰撞。

<PUBLIC:COMPONENTtagName="COMBOBOX">...
 <STYLETYPE="text/css">. clsTextField {border:none;margin-right:1px;margin-left:1px;}
. clsTextFieldCell {background-color:white;border:ridge 1px buttonface;}
. clsTextFieldCell_hover {background-color:white;border:solid 1px navy;}
. . .</STYLE><body><tableunselectable="on"ID="tblCombobox"cellspacing="0"cellpadding="0"border="0"><tr><tdunselectable="on"class="clsTextFieldCell"id="textFieldCell"><inputclass="clsTextField"id="textField"type="text"NAME="textField"></td><tdid="dropDownArrowCell"class="clsDropDownCell"><imgwidth=5 height=3 id="imgArrow"src="images/down_arrow.gif"vspace="2"hspace="3"/></td></tr></table></body></PUBLIC:COMPONENT>

你还将注意到,我在HTC文件中定义了CSS样式类。 在 mouseovermouseoutclick 事件处理程序中交换 className 属性。 所以如果你想修改外观,你必须在这里做。

图 1

元素的主尺寸由它的内容 Fragments 定义。 动态创建的元素,无论它是绝对定位的,影响和受到这些维度的限制。 在 ComboBox 中,我们创建了一个由 DIV 包含表示 ListItems Collection的HTML table的元素。 每个 table 单元格捕获窗口事件,如 mouseoutmouseoverclick。 当添加到元素树的主文档时,新的下降尺寸大于初始大小。 如图 1所示,ComboBox 是 inside table,当单击时,下拉将扩展 table 单元格的高度。 下拉的右边缘也被剪裁,因为滚动条没有显示。

如你所见,这不可能。 我们需要在新创建的table 中添加主文档树。 这样做允许我们绝对地定位,而不改变元素的主尺寸。 这也带来了另一个有趣的点,textField 包含在主元素中。 发布文档的主要形式时,不会在请求中发送 textField 数据。 如果你循环访问表单的Collection,你将无法找到 textField。 因这里,为了让 ComboBox 参与后台,我们必须创建一个隐藏字段并将它的附加到元素 form的主父级。

最后,我想打开 options Collection。 由于 ComboBox 下降实质上是一个 HTML table,所有状态逻辑都封装在 ComboBox 本身中,我们不需要创建传统的HTML。 选择 元素,但在服务器环境中编写 ComboBox 时,可能需要从客户端动态地附加或者删除列表项。 因此,我们必须将 Collection 公开为 public 属性。 我们可以选择创建自己的Collection 类并引入一个全新的API,或者我们只需要创建一个内存 选择 元素并将它的options Collection 映射到 public 属性。 我选择了后者,因为 选择 元素在内存中,不呈现在浏览器上,我公开了 repaint() 方法,该方法将显式地重新绘制。 这里方法需要在从客户端添加或者 delete 项之后调用。

已知问题

  • 内联对齐- ComboBox 不能与相邻文本正确对齐。 将控件 inside 放置为 table 单元格以解决这里问题。

COM  WEB  组合框  webcontrol  
相关文章