又一个ASP.NET组合框 - 第一部分





2.00/5 (3投票s)
2007 年 9 月 13 日
4分钟阅读

48204

378
使用Javascript、CSS和ASP.NET创建ASP.NET组合框
屏幕截图
图 1:组合框演示截图
下载
引言
本文演示了如何从头开始实现一个类似于Windows组合框的ASP.NET组合框控件。大部分编程逻辑实际上驻留在客户端,因为JavaScript负责大部分数据处理。该控件是使用JavaScript、CSS和ASP.NET构建的。目标是创建一个多功能Web控件,其行为类似于文本框和下拉列表,并支持自动建议(第二部分),以及能够将日历、树、网格或其他自定义控件作为菜单选项。因此,它被命名为“Combox”。
组合框控件中的文件
zip文件包含此控件的以下文件
- combox.ascx
组合框控件设计器用户前端,包含Javascript、CSS和HTML - combox.ascx.vb
组合框控件ASP.NET代码隐藏
- combox_arrowdown.gif
组合框下拉菜单的GIF图像
- combox_arrowdown_over.gif
鼠标悬停在组合框控件上时,用于组合框下拉菜单的GIF图像
代码
组合框由四个主要组件构成
- Javascript
- CSS
- HTML
- VB.NET
为了实现最大的灵活性,我选择使用HTML块元素DIV而不是ASP.NET ListBox来承载菜单选项,这样您就可以包含图像或任何HTML代码等元素作为菜单项。
HTML代码(文件:combox.ascx)
<div id="cmbCtrl">
<div id="cmbMain">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<div id="cmbTextField">
<asp:textbox id="txtComboxText" name="txtComboxText"
runat="server" style="border:0px" autocomplete="off" />
</div>
</td>
<td><div id="cmbImgArrow"> </div>
</td>
</tr>
</table>
</div>
<div id="cmbOptions" onclick="CZ_COMBOX.getTargetElmTextValue(event, this);">
<asp:placeholder id="phCmbOptions" runat="server">
<asp:Literal id="litCmbOptions" runat="server" />
</asp:placeholder>
</div>
</div>
一瞥之下,上面的HTML反映了下面的UI
图 2:组合框UI
让我们逐步了解创建组合框用户界面的HTML。它包含两个主要的DIV块,cmbMain和cmbOptions。
cmbMain包含一个HTML表格,其中包含单行两列。DIV cmbTextField包含ASP.NET Textbox控件txtComboxText。cmImgArrow将包含用作下拉箭头的GIF图像,仅用于美观。cmbTextField创建组合框的文本框部分,cmbOptions承载菜单选项。请注意,autocomplete功能已设置为off,因此它不会干扰我们的下拉列表。另外,请注意每个TD是如何被包含在<DIV>块中的,以便CSS可以引用它,这将在本文后面解释。
DIV cmbOptions承载菜单选项。每当用户从下拉列表中选择一个项目时,就会触发onclick事件,并由我们的javascript getTargetElmTextValue(event, this)事件处理程序方法处理。
我们将在本文的后面部分讨论<asp:placeholder>。它实际上只是一个用于承载菜单项的占位符。
Javascript代码(文件:combox.ascx)
// CZ_COMBOX here is merely served the namespace
var CZ_COMBOX = {
// if onclick event target is not menu or imgArw, then hide the menu
checkTargetVisiblity: function(e){
var target = (e && e.target) || (event && event.srcElement);
var oOption = document.getElementById("cmbOptions");
var oImgArw = document.getElementById("cmbImgArrow");
// if(target == oOption || target == oImgArw)
if(target == oImgArw)
oOption.style.visibility = 'visible';
else
oOption.style.visibility = 'hidden';
},
// trim white space
trim: function(txt){
return txt.replace(/^\s+|\s+$/g,"");
},
// get text from the target/source element.
// Note, we issue kill event bubble because in this case we need event bubbling from the inner elements.
getTargetElmTextValue: function(e, obj){
var trg;
if(!e) e = window.event;
if(e.target) trg = e.target;
else if(e.srcElement) trg = e.srcElement;
// if(trg != this) return;
document.forms[0].<%= txtComboxText.ClientID %>
上面的Javascript是组合框控件的核心。希望您对Javascript有一定的了解。如果您对Javascript相对陌生,我推荐阅读“使用面向对象技术创建高级Web应用程序”,这是一篇出色的Javascript OOP速成教程。它也包含在我最近的博客文章中。
CZ_COMBOX是用于防止与其他控件一起使用时发生名称冲突的命名空间。
checkTargetVisiblity(e) 在菜单外部单击鼠标时隐藏菜单选项。
trim() 类似于VB的Trim函数,用于删除文本周围多余的空格。
getTargetElmTextValue(e, obj) 在单击菜单项时返回目标/源元素的文本。控件ID使用服务器端VB.NET <%= txtComboxText.ClientID %>检索。当EnableAutoPostBack(稍后介绍)设置为True时,该函数还将提交表单。
getInnerText(elt) 返回目标源的innerText(IE)或innerHTML(FireFox/Mozilla)。
isTextNode(node) 确定一个节点是否为文本节点。
最后,document.onclick = CZ_COMBOX.checkTargetVisiblity 重写了全局onclick事件。因此,每当发生鼠标单击时,都会调用checkTargetVisibility()函数。
CSS(文件:combox.ascx)
DIV#cmbMain TABLE{
border:1px solid lightgrey;
}
DIV#cmbCtrl INPUT{
height:16px;
font-size:8pt;
margin-left:2px;
}
DIV#cmbImgArrow{
width:17px;
height:20px;
background-image:url('images/combox_arrowdown.gif');
}
DIV#cmbImgArrow:hover{
background-image:url('images/combox_arrowdown_over.gif') ;
}
DIV#cmbOptions{
border:1px solid lightgrey;
width:250px;
width:<%= CType(Width, Integer) + 20 %>px;
background:white;
cursor:pointer;
visibility:hidden;
overflow:auto;
position:absolute;
z-index:1000;
font-size:9pt;
}
DIV#cmbOptions A {
display:block;
height:<%= Height %>px; /* default height is nothing */
text-decoration:none;
white-space;nowrap;
color:#000000;
}
DIV#cmbOptions A:hover {
display:block;
background-color:#FFFFC5;
background-color:<%= HighlightColor%>;
cursor:pointer;
}
DIV#cmbOptions A IMG {
border:0;
}
DIV#cmbTextField input{
width:250px; /* default width */
width:<%= Width %>px;
}
.hideDIV{display:none;}
.showDIV{display:block;}
好的。您可能认为这些Javascript还不错,直到您看到这个控件所需的级联样式表(CSS)。没什么好害怕的!虽然CSS可能会变得非常复杂和令人困惑,但对于我们这个小小的组合框控件来说,它实际上并不算太糟糕。以下是如何阅读它:
每个样式都以“DIV#<ID NAME>”开头。井号后面的文本是ID的名称。例如,DIV#cmbMain指的是ID等于cmbMain的块元素,而DIV#cmbOptions A IMG指的是包含在ID等于cmbOptions的块元素中的超链接内的IMG标签。
所以基本上,找到每个对应的DIV,然后逐行阅读,以了解每一行对每个特定元素的作用。例如,在DIV#cmbOptions中,您会注意到visibility设置为“hidden”,overflow设置为“auto”,所以您会知道最初菜单选项被设置为隐藏。然而,一旦菜单项显示出来,它们就会覆盖页面上的其他控件。很简单吧?:)
代码隐藏(文件:combox.ascx.vb)
Public Class combox
Inherits System.Web.UI.UserControl
Protected WithEvents phCmbOptions As System.Web.UI.WebControls.PlaceHolder
Protected WithEvents litCmbOptions As System.Web.UI.WebControls.Literal
Protected CmbOptionsCollection As New System.Collections.Specialized.NameValueCollection
Private _cmbOptionsTemplate As ITemplate = Nothing
Private _enableAutoPostBack As Boolean
Private _highlightColor As String
Private _width As String
Private _height As String
#Region " Web Form Designer Generated Code "
'This call is required by the Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object
#End Region
<TemplateContainer(GetType(ComboxOptionsContainer))> _
Public Property CmbOptionsTemplate() As ITemplate
Get
Return _cmbOptionsTemplate
End Get
Set(ByVal Value As ITemplate)
_cmbOptionsTemplate = Value
End Set
End Property
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
If Not (CmbOptionsTemplate Is Nothing) Then
Dim container As New ComboxOptionsContainer
CmbOptionsTemplate.InstantiateIn(container)
phCmbOptions.Controls.Add(container)
End If
End Sub
Public Property EnableAutoPostBack() As Boolean
Get
Return _enableAutoPostBack
End Get
Set(ByVal Value As Boolean)
_enableAutoPostBack = Value
End Set
End Property
Public Property HighlightColor() As String
Get
Return _highlightColor
End Get
Set(ByVal Value As String)
_highlightColor = Value
End Set
End Property
Public Property Width() As String
Get
Return _width
End Get
Set(ByVal Value As String)
_width = Value
End Set
End Property
Public Property Height() As String
Get
Return _height
End Get
Set(ByVal Value As String)
_height = Value
End Set
End Property
Public Sub AddOption(ByVal key As String, ByVal text As String)
CmbOptionsCollection.Add(key, text)
End Sub
Public Sub RemoveOption(ByVal key As String)
CmbOptionsCollection.Remove(key)
End Sub
Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
Dim strCmbOptions As New System.Text.StringBuilder
For Each key As String In CmbOptionsCollection
strCmbOptions.Append("<div key=""")
strCmbOptions.Append(key)
strCmbOptions.Append(""">")
strCmbOptions.Append(CmbOptionsCollection(key))
strCmbOptions.Append("</div>")
strCmbOptions.Append(vbCrLf)
Next
litCmbOptions.Text = strCmbOptions.ToString
End Sub
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
End Sub
Protected Overrides Function SaveViewState() As Object
End Function
#Region "Container"
Public Class ComboxOptionsContainer
Inherits Control
Implements INamingContainer
Private _OptKey As String
Private _OptText As String
Public Property OptKey() As String
Get
Return _OptKey
End Get
Set(ByVal Value As String)
_OptKey = Value
End Set
End Property
Public Property OptText() As String
Get
Return _OptText
End Get
Set(ByVal Value As String)
_OptText = Value
End Set
End Property
Public Sub New()
_OptKey = ""
_OptText = ""
End Sub
Public Sub New(ByVal newKey As String, ByVal newText As String)
_OptKey = newKey
_OptText = newText
End Sub
End Class
#End Region
End Class
令人惊讶的是,整个实现中最简单的一部分是ASP.NET代码隐藏。
使用代码
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="test.aspx.vb" Inherits="combox.test"%>
<%@ Register TagPrefix="ps" TagName="Combox" Src="combox.ascx" %>
<html>
<head>
<title>Combox Demo</title>
<style>
.flag
{
position:relative;
top:3px;
margin-right:3px;
}
</style>
</head>
<body>
<form id="Form1" method="post" runat="server">
<ps:Combox id="psCombox" EnableAutoPostBack="False" Width="180" HighlightColor="#AABBEB" runat="server">
<CmbOptionsTemplate>
<div key="USA"><img src="flags/usa.gif" class="flag" />United States</div>
<div key="UK"><img src="flags/uk.gif" class="flag" />United Kingdom</div>
<div key="CAN"><img src="flags/canada.gif" class="flag" />Canada</div>
<div key="CHN"><img src="flags/china.gif" class="flag" />China</div>
<div key="FRN"><img src="flags/france.gif" class="flag" />France</div>
<div key="SWD"><img src="flags/sweden.gif" class="flag" />Sweden</div>
<div key="HK"><img src="flags/hongkong.gif" class="flag" />Hong Kong</div>
<div key="POL"><img src="flags/poland.gif" class="flag" />Poland</div>
</CmbOptionsTemplate>
</ps:combox>
</form>
</body>
</html>
下一步
创建此类自定义用户控件的主要挑战在于,它需要广泛的技能,不仅包括ASP.NET代码隐藏,还需要HTML、CSS,尤其是Javascript,以及对UI设计的关注。我不是Javascript专家,但这主要是一个学习经验,也是为了分享我所知道的,并希望从我自己的错误和别人的经验中学习。
暂时就到这里。一旦有时间,我将在此文章中添加更多细节。