一个模板化的“请稍候”按钮,以及模板控件介绍






4.21/5 (12投票s)
本文旨在介绍一个模板化控件的构建,该控件可作为“请稍候”按钮使用。
1. 引言
本文介绍了模板化 ASP.NET 控件的构建,该控件可作为“请稍候”按钮使用。使用 C# 和 ASP.NET 2.0,您将了解如何创建模板化控件并添加特定功能。
有时,您需要在执行耗时任务时,例如在按钮单击事件中提交页面时。有时,表单验证可能需要调用数据库或远程调用 Web 服务……为了告知用户服务器正在执行耗时操作,最好显示等待消息。
我看到过一些文章,它们重新创建了一个带有附加属性的按钮,该按钮可以显示消息。但其呈现方式始终不符合我的要求。因此,我决定创建一个功能完整、可通过模板功能进行自定义的控件。
2. 目标结果
我希望在我的 ASP.NET 页面中得到类似这样的效果
<Test:PleaseWaitButton ID="PWB" runat="server">
<PleaseWaitMessageTemplate>
<div style="width:500px;color:green;border:1px solid red;text-align:center">
<div>Please wait a moment, </div>
<div>we are checking your informations ...</div>
<asp:Image runat="server" ImageUrl="wait.gif" />
</div>
</PleaseWaitMessageTemplate>
</Test:PleaseWaitButton>
然后,我们有按钮,以及定义“请稍候”消息图形化显示的地方。
为了看起来像一个真正的按钮,我们需要在按钮单击事件上定义一个事件处理程序。我们希望能够通过 Text
属性来自定义按钮的文本。
在表单中,我们通常希望使用 ASP.NET 验证。然后,按钮需要检查此验证,并在需要时定义一个 ValidationGroup
属性。
<Test:PleaseWaitButton ID="PWB" runat="server"
OnClick="ClickButton" ValidationGroup="myGroup"
Text="Please Click">
<PleaseWaitMessageTemplate>
<div style="width:500px;color:green;border:1px solid red;text-align:center">
<div>Please wait a moment, </div>
<div>we are checking your informations ...</div>
<asp:Image runat="server" ImageUrl="wait.gif" />
</div>
</PleaseWaitMessageTemplate>
</Test:PleaseWaitButton>
3. 创建模板按钮
首先,您必须继承自 Control
并实现 INamingContainer
。您还必须定义 ParseChildren(true)
属性。
[System.Security.Permissions.PermissionSet(
System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
[ParseChildren(true)]
[DefaultProperty("Text")]
public class PleaseWaitButton : Control, INamingContainer
{
}
3.1. ITemplate 属性
这就是您所需要的一切:拥有一个模板属性。PersistenceMode
用于告诉解析器该属性作为内部属性持久化。TemplateContainer
在此处未使用,它用于数据绑定类型。
private ITemplate _pleaseWaitMessageTemplate = null;
[Browsable(false), DefaultValue(null),
PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(TemplateItem))]
public ITemplate PleaseWaitMessageTemplate
{
get { return _pleaseWaitMessageTemplate; }
set { _pleaseWaitMessageTemplate = value; }
}
我们还需要定义一个 TemplateItemp
类。
// TemplateItem should implement INamingContainer
[ToolboxItem(false)]
public class TemplateItem : Control, INamingContainer
{
}
4.2. 控件的创建
我们将重写 CreateChildControls
方法来创建一个 Panel
来包含我们的控件。在此 Panel
中,如果用户定义了模板,我们将实例化它(作为一个 TemplateItem
)。如果未定义模板,我们将创建一个默认的 LiteralControl
并带有默认消息。
紧接着,我们添加一个按钮,该按钮定义了 Click
事件处理程序,并将 Text 属性设置为我们的 Text
属性。
使用 CSS 样式,我们将 Panel
设置为 display: none。按钮(在 Panel 之外)始终可见。
protected override void CreateChildControls()
{
Controls.Clear();
// Create an hidden panel
Panel panelMessage = new Panel();
panelMessage.Attributes["style"] = "display:none";
if (PleaseWaitMessageTemplate != null)
{
// if a template is defined, use it
TemplateItem templateItem = new TemplateItem();
PleaseWaitMessageTemplate.InstantiateIn(templateItem);
panelMessage.Controls.Add(templateItem);
}
else
{
// else, create a default ugly message
panelMessage.Controls.Add(new LiteralControl("Plesae Wait ..."));
}
// Button is created with Text property
// and a click handler
Button boutonValidation = new Button();
boutonValidation.Text = Text;
boutonValidation.Click += b_Click;
// Then add panel and button
Controls.Add(panelMessage);
Controls.Add(boutonValidation);
}
4.3. 属性的创建
如前所述,我们需要保留一个 ValidationGroup
属性和一个 Text
属性。如果没有定义文本,我们将使用默认值“OK”。我们还定义了一个 Click
事件。为了确保控件的创建,让我们重写 Controls
属性并调用 EnsureChildControls()
(这将根据需要调用 CreateChildControls
)。
[Bindable(true), Category("Behavior"), Description("Validation group")]
public string ValidationGroup
{
get { return (string)ViewState["ValidationGroup"] ?? string.Empty; }
set { ViewState["ValidationGroup"] = value; }
}
[Bindable(true), Category("Appearance"),
DefaultValue("OK"), Description("Button's text")]
public string Text
{
get { return (string)ViewState["Text"] ?? "OK"; }
set { ViewState["Text"] = value; }
}
private event EventHandler _clickHandler;
public event EventHandler Click
{
add { _clickHandler += value; }
remove { _clickHandler -= value; }
}
public override ControlCollection Controls
{
get { EnsureChildControls(); return base.Controls; }
}
4.4. 验证
我们必须请求页面验证(使用 Page.Validate
方法)。如果定义了 ValidationGroup
,我们将请求该组的验证。
private void b_Click(object sender, EventArgs e)
{
// manual validation
if (!string.IsNullOrEmpty(ValidationGroup))
Page.Validate(ValidationGroup);
else
Page.Validate();
// Fire the user-define click event, if defined
if (_clickHandler != null)
_clickHandler(sender, e);
}
4.4. 客户端 JavaScript
在客户端,我们需要做两件事:
- 客户端验证
- 显示/隐藏“请稍候”消息
这可以通过 JavaScript 实现。我们将根据验证结果(“OK”或“NOK”)来操作 Panel(在 HTML 中表示为 div
)的显示样式。
因此,让我们在渲染阶段添加一些 JavaScript,以便在客户端按钮单击事件上调用特定的 JavaScript 函数(请参见注释)。
但首先,我们必须找到 Panel
的 clientId
。
protected override void OnPreRender(EventArgs e)
{
// we look for the panel and set an ID
// warning : this operation can't be done
// in CreateChildControls, it would have been too earl
Panel panelMessage = null;
foreach (Control control in Controls)
{
if (control is Panel)
{
control.ID = ID + "_waiting";
panelMessage = (Panel) control;
}
}
// When panel founded, look for the button,
// and add a clientside function
//(with panel clientId as parameters)
if (panelMessage != null)
{
foreach (Control control in Controls)
{
if (control is Button)
{
((Button) control).OnClientClick =
string.Format("checkForm('{0}')", panelMessage.ClientID);
}
}
}
base.OnPreRender(e);
}
在渲染时,创建将根据客户端验证结果(客户端验证通过 Page_ClientValidate
方法完成)来管理显示的 JavaScript 函数。
protected override void Render(HtmlTextWriter writer)
{
// script client-side creation :
// If there's a validation group,
// call validation function with the group as parameters
// else without parameters
string validationGroupParameters = string.IsNullOrEmpty(ValidationGroup) ?
string.Empty : string.Format("'{0}'", ValidationGroup);
// if validation is OK, display panel (and waiting message)
// if validation NOK, hide panel and return false
string script = @"function getObj(id)
{
var o;
if (document.getElementById)
{
o = document.getElementById(id).style;
}
else if (document.layers)
{
o = document.layers[id];
}
else if (document.all)
{
o = document.all[id].style;
}
return o;
}
function setDisplay(id)
{
var o = getObj(id);
if (o)
{
o.display = 'block';
}
}
function unsetDisplay(id)
{
var o = getObj(id);
if (o)
{
o.display = 'none';
}
}
function checkForm(divWaiting)
{
try
{
if (!Page_ClientValidate(" + validationGroupParameters + @"))
{
unsetDisplay(divWaiting);
return false;
}
}
catch (e) {}
setDisplay(divWaiting);
}";
Page.ClientScript.RegisterStartupScript(GetType(),
"javascriptButton", script, true);
base.Render(writer);
}
4. 创建一个使用此按钮的默认页面
4.1. 无验证
只需定义模板控件和模板属性。如果需要,添加按钮单击事件处理程序。
<Test:PleaseWaitButton ID="PWB" runat="server" OnClick="ClickButton">
<PleaseWaitMessageTemplate>
<div style="width:500px;color:green;border:1px solid red;text-align:center">
<div>Please wait a moment, </div>
<div>we are checking your informations ...</div>
<asp:Image runat="server" ImageUrl="wait.gif" />
</div>
</PleaseWaitMessageTemplate>
</Test:PleaseWaitButton>
在代码隐藏中,模拟要执行的耗时任务……
protected void ClickButton(object sender, EventArgs e)
{
Thread.Sleep(2000);
}
4.2. 带验证
4.2.1. 无验证组
让我们定义一个 TextBox
,并添加客户端验证(使用 RequiredFieldValidator
控件)和服务器端验证(使用 CustomValidator
控件)。
<asp:TextBox runat="server" ID="aTextBox" />
<asp:RequiredFieldValidator runat="server" ControlToValidate="aTextBox"
ErrorMessage="Field should have a value" Display="dynamic"/>
<asp:CustomValidator runat="server" OnServerValidate="ValidateFunction"
Display="dynamic" ErrorMessage="Value should be ABC" />
protected void ClickButton(object sender, EventArgs e)
{
if (Page.IsValid)
{
Thread.Sleep(2000);
Response.Write("<br/>Informations are OK<br/>");
}
}
protected void ValidateFunction(object source, ServerValidateEventArgs args)
{
args.IsValid = aTextBox.Text == "ABC";
}
4.2.2. 带验证组
您也可以尝试使用验证组,如下所示:
<asp:TextBox runat="server" ID="aTextBox" />
<asp:RequiredFieldValidator runat="server" ControlToValidate="aTextBox"
ErrorMessage="Field should have a value"
Display="dynamic" ValidationGroup="myGroup"/>
<asp:CustomValidator runat="server" OnServerValidate="ValidateFunction"
Display="dynamic" ErrorMessage="Value should be ABC"
ValidationGroup="myGroup"/>
<Test:PleaseWaitButton ID="PWB" runat="server"
OnClick="ClickButton" ValidationGroup="myGroup">
<PleaseWaitMessageTemplate>
<div style="width:500px;color:green;border:1px solid red;text-align:center">
<div>Please wait a moment, </div>
<div>we are checking your informations ...</div>
<asp:Image runat="server" ImageUrl="wait.gif" />
</div>
</PleaseWaitMessageTemplate>
</Test:PleaseWaitButton>
protected void ClickButton(object sender, EventArgs e)
{
if (Page.IsValid)
{
Thread.Sleep(2000);
Response.Write("<br/>Informations are OK<br/>");
}
}
protected void ValidateFunction(object source, ServerValidateEventArgs args)
{
args.IsValid = aTextBox.Text == "ABC";
}
5. 预览
6. 结论
我希望您觉得这个模板按钮很有用,并且可能学会如何在 ASP.NET 中使用 C# 创建模板控件。验证是一个关键点,在 ASP.NET 页面中非常有用。了解如何利用验证 API 进行验证也会非常强大。
如果您在此文章、源代码或任何其他信息中发现错误,请给我留言。