ASP.NET AJAX Web Applications 中的客户端编辑模式






3.24/5 (6投票s)
本文介绍了一种构建 ASP.NET 页面客户端编辑模式的方法:响应更改(用户或代码),可视化模式(编辑/未更改)。
引言
本文讨论了一种构建 ASP.NET 页面客户端编辑模式的方法。编辑模式意味着当用户开始更改页面上的数据时,页面上会出现一些视觉变化。
在我简易的示例 Web 应用程序的这张截图中,您可以看到一个用户可以输入数据的页面。请注意,我在页面上放置了 ASP.NET 2.0 的各种基本服务器控件(TextBox、ComboBox、RadioButton 等)。

在下一个截图中,用户开始输入数据。应用程序现在通过在页面顶部显示醒目的红色正在编辑文本来指示页面上有未保存的更改。此外,保存按钮已启用。
由于所有操作都在客户端进行,因此没有任何回发伪像
- 键入 TextBox时没有延迟
- 键入 TextBox时光标位置没有重置
- 没有闪烁

当用户单击“保存”按钮时,醒目的红色正在编辑文本将消失,“保存”按钮将再次禁用。
最新更新
使用 AJAX Control Toolkit 构建,版本 10618。
基础知识
这种行为的基础是三个 Extender(继承自 AjaxControlToolkit.ExtenderControlBase)
- EditExtender:此控件通过添加客户端 JavaScript 来扩展输入控件(- TextBox、- CheckBox等),该 JavaScript 响应用户输入并通知- EditAnimationExtender已发生更改。
- ButtonEndEditExtender:此控件扩展按钮并通知- EditAnimationExtender应结束编辑。
- EditAnimationExtender:此控件定义了在开始和结束编辑模式时执行的操作。
下面的截图显示了我的示例应用程序在 Visual Studio 设计器中的页面

Extender
本节简要概述了使用的 Extender。
EditExtender
EditExtender 控件用于以这样的方式扩展控件,即输入数据将启动编辑模式。
| [TargetControlType(typeof(Control))] EditExtender : AjaxControlToolkit.ExtenderControlBase | |
|---|---|
| 属性 | 描述 | 
| string EditAnimationID | 当用户修改扩展控件上的数据时,要通知哪个 EditAnimationExtender。 | 
EditExtender 可以扩展单个输入控件或容器控件(Panel、UserControl 等)。根据被扩展控件的类型,Extender 会注册用于指示数据修改的事件。
| 控件类型 | 事件 | 
|---|---|
| 文本 | keyup | 
| select-one | change | 
| radio | click | 
| checkbox | click | 
当触发相应的事件时,会通知关联的 EditAnimationExtender 数据已被修改。
ButtonEndEditExtender
ButtonEndEditExtender 控件用于扩展 Button,以便单击它将结束编辑模式。
| [TargetControlType(typeof(IButtonControl))] ButtonEndEditExtender : AjaxControlToolkit.ExtenderControlBase | |
|---|---|
| 属性 | 描述 | 
| string EditAnimationID | 当用户单击扩展的 Button时,要通知哪个EditAnimationExtender结束编辑。 | 
| bool SuppressPostBack | 是否应抑制扩展控件的回发。如果设置为 true,则不会发生回发。 | 
Extender 会注册被扩展控件的 click 事件。当用户单击扩展的 Button 时,会通知关联的 EditAnimationExtender 应结束编辑模式。
EditAnimationExtender
这是负责执行开始或结束编辑模式所需操作的主要 Extender。
| [TargetControlType(typeof(Control))] EditAnimationExtender : AjaxControlToolkit.ExtenderControlBase | |
|---|---|
| 属性 | 描述 | 
| AjaxControlToolkit.Animation OnEditing | 开始编辑模式时播放的动画。 | 
| AjaxControlToolkit.Animation OnEdited | 结束编辑模式时播放的动画。 | 
| bool ShowHideTargetControl | 根据编辑模式是否显示/隐藏扩展控件。当您希望在编辑模式下显示控件时,将其设置为 true。 | 
| bool NotifyOtherEditAnimationExtenders | 是否应通知同一页面上的其他 EditAnimationExtender关于开始/结束编辑模式。这对于同步 UserControl 和宿主页面非常有用。 | 
Extender 第一次收到 EditExtender 关于修改的通知时,Extender 会播放 OnEditing 动画并显示被扩展控件(如果 ShowHideTargetControl 等于 true)。
当 Extender 收到 ButtonEndEditExtender 的通知,表示用户希望结束编辑模式(通常通过单击“保存”或“取消”按钮)时,将播放 OnEdited 动画并隐藏被扩展控件(如果 ShowHideTargetControl 等于 true)。
Extender 会维护其状态。这意味着动画仅在编辑模式切换时播放,并且状态会在回发之间保持。也可以在服务器端代码中请求或设置状态(有关更多信息,请参阅下面的“跨越回发”部分)。
当 NotifyOtherEditAnimationExtenders 设置为 true 时,每次编辑模式切换时,页面上的所有其他 EditAnimationExtender 都会收到状态切换的通知,并播放其关联的动画。当您的页面上有嵌套的 UserControl 时,这非常有用,并且您希望在页面上任何位置修改数据都会导致所有 EditAnimationExtender 相应地切换其编辑模式。
高级主题 (Advanced Topics)
倾听用户!
只有一种类型的 EditExtender。因此,它必须处理所有不同的输入控件。此外,它可以通过在容器的所有子控件上注册输入事件来处理容器控件。这最大限度地减少了页面或 UserControl 上所需的 EditExtender 数量。
下面显示了用于注册输入事件的 JavaScript 代码。
registerEvents : function(root)
{
    switch (root.type)
    {
        case 'text':
            $addHandler(root, 'keyup', Function.createDelegate(this, this._onedit));
            break;
    
        case 'select-one':
            $addHandler(root, 'change', Function.createDelegate(this, this._onedit));
            break;
            
        case 'radio':
        case 'checkbox':
            $addHandler(root, 'click', Function.createDelegate(this, this._onedit));
            break;
            
        default:   
                var child = root.firstChild;
                while (child)
                {
                    this.registerEvents(child);
                    child = child.nextSibling;
                }
            break;
    }
},
如上所示,_onedit 函数被注册为输入事件的处理程序。当输入事件触发时,EditExtender 通过通知 EditAnimationExtender 来响应。
如下面的代码所示,通过使用 $find 函数来访问 EditAnimationExtender。
_onedit : function(e) {
    if (e.keyCode != Sys.UI.Key.tab)
    {
        [... some code ...]
        
        var animation = $find(this._editAnimationIDValue);
        if (animation) {
            animation.startEdit(true);
        }
    }
},
跨越回发或传递关于当前模式的信息,从客户端到服务器,反之亦然
回发后,页面会全部或部分重新渲染。这可能会撤销动画在 EditAnimationExtender 中定义的 OnEditing 更改。因此,您应该确保相应的 EditAnimationExtender 也被重新渲染。您可以通过将 Extender 放在与重新渲染内容相同的 UpdatePanel 中来实现。
现在发生的情况是,当 EditAnimationExtender 被重新渲染时,它将恢复其最后的 EditMode 状态:如果之前处于编辑状态,则播放 OnEditing 动画。
为了在回发之间维护编辑状态,Extender 使用 AJAX Toolkit 的 ExtenderControlBase 类的 ClientState 功能。
首先,我们需要在 EditAnimationExtender 中启用和初始化 ClientState。
public class EditAnimationExtender : AnimationExtenderControlBase
{
    public EditAnimationExtender()
    {
        this.EnableClientState = true;
        this.ClientState = "false";
        
        [... other code ...]
    }
    
    [... other code ...]
}
然后,在 Extender 初始化时,我们可以在客户端访问 ClientState,并根据当前状态进行响应。
initialize : function() {
    bbv.Ajax.ControlExtender.Edit.EditAnimationBehavior.callBaseMethod(this, 'initialize');
    this._onEditing.initialize();
    this._onEdited.initialize();
    
    // restore editing state
    var state = this.get_ClientState();
    if (state)
    {
        state = Boolean.parse(state);  
    }
    if (state)
    {
        if (this._showHideTargetControlValue) {
            this.get_element().style.visibility = 'Visible'
        }
        this.startEdit(true);
    }
    else
    {
        this.endEdit(true);
    }
},
最后,我们必须在状态在客户端更改时设置该状态。
startEdit : function(relay)
{
    if (!this._isEditing)
    {
        [... other code ...]
        
        this._isEditing = true;
        this.set_ClientState('true');
        
        [... other code ...]
    }
},
此外,我们现在可以从服务器端开始编辑。
private void StartEditing()
{
    this.EditAnimationExtender.IsEditing = true;
    this.UpdatePanel.Update();
}
同步嵌套 UserControl
我们的 Web 应用程序中存在一些复杂的 UI,它们由多个 UserControl 构建而成。每个 UserControl 都有自己的 EditAnimationExtender,以将控件引用的范围保留在 UserControl 内部。因此,不同的 EditAnimationExtender 必须相互通信,才能让整个页面参与编辑模式。
当一个 EditAnimationExtender 切换其编辑模式时,它会通知页面上的所有其他 EditAnimationExtender。
startEdit : function(relay)
{
    if (!this._isEditing)
    {
        [... other code ...]
        
        if (relay && this._notifyOtherEditAnimationExtendersValue) {
            var c = Sys.Application.getComponents();
            for (var i = 0; i < c.length; i++) {
                var id = c[i].get_id();
                var type = Object.getType(c[i]).getName();
                
                if (type == "bbv.Ajax.ControlExtender.Edit.EditAnimationBehavior") {
                    $find(id).startEdit(false);
                }
            }
        }
    }
},
使用 relay 参数是为了确保只有一个 EditAnimationExtender 发送通知。否则会导致无限循环。
最后,您可以将 NotifyOtherEditAnimationExtenders 属性设置为 false,以获得一个独立的 EditAnimationExtender,它不会通知其他 EditAnimationExtender。
已知问题
从服务器端启动编辑模式需要 UpdatePanel 更新
当您想在服务器端代码中切换到 EditMode 时,您必须在页面上设置 EditAnimationExtender 的 IsEditing 属性,并更新托管此 EditAnimationExtender 的 UpdatePanel。这样,Extender 将被重新初始化,并在客户端进入 EditMode。
动画重播
EditAnimationExtender 在页面(部分)重新渲染后会重置其状态,并播放开始或结束动画。因此,您应该确保这些动画可以播放和停止,而不会相互干扰。
结论
本文介绍了一种维护客户端编辑模式的方法,该方法可用于获得更好的用户体验。
此代码摘自一个正在开发中的项目。如果您发现任何问题、故障或有扩展想法,请告诉我。
代码来源
示例代码摘自我为 bbv Software Services AG 编写的一个 Web 应用程序。
历史
- 2007-06-06:初始版本。
- 2007-08-10:使用 AJAX Control Toolkit 构建,版本 10618。


