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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.21/5 (12投票s)

2008年3月25日

CPOL

4分钟阅读

viewsIcon

72726

downloadIcon

604

本文旨在介绍一个模板化控件的构建,该控件可作为“请稍候”按钮使用。

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 函数(请参见注释)。

但首先,我们必须找到 PanelclientId

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 进行验证也会非常强大。

如果您在此文章、源代码或任何其他信息中发现错误,请给我留言。

© . All rights reserved.