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

修改和使用 PopupControlExtender 创建嵌套弹出窗口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2011 年 4 月 20 日

CPOL

5分钟阅读

viewsIcon

76277

downloadIcon

3756

改进的 AJAX 控件工具包 PopupControlExtender 控件及其使用方法,用于创建嵌套弹出控件。

Popup 1 of nested popups

图 1:页面

Popup 1 of nested popups

图 2:弹出窗口 1

Popup 2 of nested popups

图 3:弹出窗口 2

Popup 2 of nested popups

图 4:弹出窗口 3

引言

本文介绍了一个改进的 AJAX 控件工具包 PopupControlExtender 控件,以及如何使用它来创建嵌套弹出控件。

背景

PopupControlExtender 是一个有用的用户界面控件,可以改善 Web 应用程序的用户体验。使用 PopupControlExtender,您可以点击页面上的其他部分来取消操作并关闭打开的弹出控件,甚至可以使用同一次点击打开另一个弹出控件。弹出控件广泛用于实现数据选择控件,例如自定义组合框、日期选择器、用户名选择器等。

PopupControlExtender 本身对于大多数场景已经足够强大,但它一次只能打开一个弹出窗口,因此您无法创建嵌套的弹出控件。如果您尝试在一个已打开的弹出窗口内打开另一个弹出窗口,已打开的弹出窗口将被关闭,而新的弹出窗口将不会显示。

为了解决这个问题,我对 PopupControlExtender 控件进行了一些改进,使其能够同时打开多个弹出控件。

使用代码

下载演示项目,先进行尝试,然后可以直接将演示项目中 bin 目录下的 AjaxControlToolkit.dll 引用到您的项目中。基于 2011 年 4 月 1 日发布的源代码,我创建了 .NET 3.5 的调试版 AjaxControlToolkit.dll。如果您需要发布版 DLL 或 .NET 4.0 的 DLL,请下载 AJAX 控件工具包的源代码,搜索并替换文章附件中的 PopupControlBehavior.pre.js 文件,然后进行构建。

这是演示项目中的一段代码示例

Date:<asp:TextBox ID="TextBox1" Width="100px" autocomplete="off" 
                  ReadOnly="false" runat="server"></asp:TextBox>
<asp:Panel ID="Panel1" Style="display: none;" runat="server">
    <div style="background-color: #7777cc; border: solid 2px #234389; 
                width: 400px; padding: 10px;">
        <div style="padding: 2px; background-color: #234389; 
                    color: White; font-weight: bold;">
            Popup1 (yyyy-MM-dd)</div>
        <br />
        <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="server">
            <ContentTemplate>
                <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
                  <span style="color: White;">(Please select Year and Month)</span>
                <asp:Panel ID="Panel2" runat="server">
                    <!-- content of Panel2 was omitted -->
                </asp:Panel>
                <asp:PopupControlExtender ID="PopupControlExtender2" 
                    PopupControlID="Panel2" TargetControlID="TextBox2"
                    Position="Bottom" runat="server">
                </asp:PopupControlExtender>
                <br />
                <br />
                <span style="color: White;">Day:</span>
                <asp:TextBox ID="TextBox2_Other1" runat="server"></asp:TextBox>
                <asp:Button ID="Button2" OnClick="Button2_Click" 
                     runat="server" UseSubmitBehavior="false" Text="Button2" />
                <br />
                <br />
                <asp:Button ID="ButtonOk1" runat="server" UseSubmitBehavior="false" 
                        Text="Ok1" OnClick="ButtonOk1_Click" />
                <asp:Button ID="ButtonCancel1" runat="server" UseSubmitBehavior="false" 
                        Text="Cancel1" OnClick="ButtonCancel1_Click" />
                <div style="width:10px; height:90px;"></div>
                <asp:TextBox ID="TextBox2_2" runat="server"></asp:TextBox>
                   <span style="color: White;">
                     (Anthor popup in the same nested level.)</span>
                <asp:Panel ID="Panel2_2" runat="server">
                <div style="width:100px; height:60px;background-color: orange; 
                            border: solid 2px #884322;" ></div>
                </asp:Panel>
                <asp:PopupControlExtender ID="PopupControlExtender2_2" 
                    PopupControlID="Panel2_2" TargetControlID="TextBox2_2"
                    Position="Bottom" runat="server">
                </asp:PopupControlExtender>
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
</asp:Panel>
<asp:PopupControlExtender ID="PopupControlExtender1" PopupControlID="Panel1" 
        TargetControlID="TextBox1" Position="Bottom" runat="server">
</asp:PopupControlExtender>

这是代码隐藏文件

#region PopupControlExtender1
protected void Button2_Click(object sender, EventArgs e)
{
    this.TextBox2_Other1.Text = DateTime.Now.Day.ToString("D2");
}

protected void ButtonOk1_Click(object sender, EventArgs e)
{
    this.PopupControlExtender1.Commit(this.TextBox2.Text + "-" + 
                     this.TextBox2_Other1.Text);
}

protected void ButtonCancel1_Click(object sender, EventArgs e)
{
    this.PopupControlExtender1.Cancel();
}
#endregion

在此演示项目中,我们创建了五个 PopupControlExtender 控件实例,它们构成了一个三级嵌套结构。我们按级别描述弹出控件。例如,在演示中,文档的 body 是第 0 级,弹出窗口“Popup1”是第 1 级,“Popup2”是第 2 级,依此类推。弹出规则如下:

  1. 如果您点击 PopupControlExtender 的目标控件,当前级别的弹出窗口将被关闭,然后 PopupControlExtender 的弹出窗口将被打开。
  2. 如果您点击一个弹出窗口,当前级别所有已打开的弹出窗口都将被关闭。
  3. 如果您点击文档的 body,所有已打开的弹出窗口都将被关闭。
  4. 在同一级别,同一时间最多只能打开一个弹出窗口。

UpdatePanel 放置在弹出控件内部是使用 PopupControlExtender 最常见的样式;否则,您将无法利用 Microsoft AJAX Framework 的优势,并且必须实现自己的客户端和服务器端逻辑来更新弹出控件的内容。在本文中,我们仅讨论使用 UpdatePanel 的情况。在演示中,所有按钮都是服务器端控件,点击这些按钮将导致部分回发。年、月、日的数值都由服务器生成。为了使 PopupControlExtender 能够正常工作,我们需要按照以下规则使用 UpdatePanel

  1. 所有 UpdatePanelUpdateMode 属性值应设置为“Conditional”。
  2. 所有 ButtonUseSubmitBehavior 属性值应设置为“false”。

实际上,使用原始的 PopupControlExtender 和改进的 PopupControlExtender 之间没有区别,除了使用改进的 PopupControlExtender,您可以将 PopupControlExtender 的目标设置在弹出控件上,并同时打开两个或多个嵌套的弹出控件。

顺便说一下,在实际项目中,您应该将这些弹出控件拆分成单独的用户控件,以使代码更具可读性、可维护性和可重用性。

对 PopupControlExtender 控件的改进

AJAX 控件工具包中的所有控件都有服务器控件和客户端行为。为了使 PopupControlBehavior 控件支持嵌套弹出窗口,我们需要修改 PopupControlExtender 控件的客户端行为,该行为定义在 PopupControlBehavior.pre.js 文件中。

如果您阅读了 PopupControlBehavior.pre.js 中的以下代码,您就会明白为什么 PopupControlExtender 一次最多只能显示一个弹出窗口。

// This global variable tracks the currently visible popup. Automatically
// hiding the popup when focus is lost does not work with our mechanism to
// hide the popup when something else is clicked... So we will instead go for
// the weaker strategy of letting at most one popup be visible at a time.
Sys.Extended.UI.PopupControlBehavior.__VisiblePopup = null;

是的,这就是它的设计方式。

在一个包含嵌套弹出控件的页面中,页面和所有弹出控件构成一个**弹出树**,文档的 body 是根节点。例如,在演示项目中,弹出树如下所示:

Popup 2 of nested popups

我们不需要创建数据结构来包含此弹出树的信息,我们只需要维护一个堆栈来记录所有已打开弹出控件的 PopupControlBehavior 对象,并按打开的顺序排列。因此,我们创建了一个静态字段,如下所示:

// @@@@@
// Tracks all the opened popups. 
Sys.Extended.UI.PopupControlBehavior.__VisiblePopups = [];

例如,当 Popup3 打开时,__VisiblePopups 数组的值如下:[Popup1, Popup2, Popup3]。

当发生点击事件时:如果一个弹出控件捕获了事件,它会阻止事件冒泡,并找出其 PopupControlBehavior 对象在 __VisiblePopups 数组中的位置,然后弹出它之上的所有 PopupControlBehavior 对象,并调用这些对象的 hidePopup 方法。如果 body 捕获了点击事件,则调用 __VisiblePopups 中所有元素的 hidePopup 方法,并清空该数组。

PopupControlExtender 的目标控件捕获到点击或焦点事件时,找出相关的 PopupControlBehavior 对象在 __VisiblePopups 数组中的位置,弹出它之上的所有 PopupControlBehavior 对象,并调用这些对象的 hidePopup 方法,然后显示新打开的弹出控件。

您可以查看源代码以获取更多详细信息。

© . All rights reserved.