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

使用 MS AJAX 持久化子 DIV 的滚动位置

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (12投票s)

2007年9月9日

CPOL

2分钟阅读

viewsIcon

105207

downloadIcon

371

一篇关于使用 MS AJAX 构建控件来持久化子 DIV 滚动位置的文章。

引言

虽然 Microsoft AJAX 中的 ScriptManagerUpdatePanel 在部分回发操作期间能很好地持久化页面的滚动位置,但您可能会惊讶地发现,对于 UpdatePanel 中包含的可滚动子 DIV,情况并非如此。

本文中介绍的 PersistentScrollPosition 控件旨在解决此问题,它使用客户端行为和使用 Microsoft AJAX 实现的 ASP.NET 服务器控件。

背景

虽然我肯定没有打算回顾 UpdatePanelPageRequestManager 的内部结构,或者实现任何客户端组件(Sys.ComponentSys.UI,BehaviorSys.UI.Control),但快速了解可以帮助您理解和解决这个特殊问题。此控件需要记住两个关键点

  1. 客户端组件在部分回发生命周期中会被销毁和重新创建,因此您不能使用控件的实例来存储任何需要在此过程中保留的数据。
  2. UpdatePanel 的 HTML 输出在部分回发期间(假设它被触发)通过 innerHTML 属性完全替换,这就是滚动位置问题首先存在的原因。

使用代码

对于那些只想获得解决方案的人来说,使用代码非常简单。该控件有一个需要设置的属性,名为 ControlToPersist。这是一个 string,它接受服务器端容器 DIV 的 ID(它必须具有 runat="server")。

<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode="always">
<ContentTemplate>
<asp:Button runat="server" ID="btnPostBack" Text="Post Back" OnClick="btnPostBack_Click" />
<br />
<div style="width:590px;height:400px;overflow-y:scroll;overflow-x:hidden;" 
     runat="server" id="persistMe">
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit...</p>

</div>
<mbc:PersistentScrollPosition runat="server" ID="psf1" ControlToPersist="persistMe" /> 
</ContentTemplate> 
</asp:UpdatePanel>

构建控件

该控件由两部分组成,这两部分在很大程度上都是非常“千篇一律”的。在服务器端,我们继承自 Control,并实现 IScriptControlINamingContainer,并在初始化期间创建一个 HiddenField,以便在部分回发之间存储我们的滚动位置。

public class PersistentScrollPosition : Control, IScriptControl, INamingContainer
{
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        // Create hidden control for storage

        storage = new HiddenField();
        storage.ID = "storage";
        Controls.Add(storage);
    }
}

在创建客户端初始化的脚本描述符时,我们传递可滚动的 DIVClientID、控件的 ElementID,并使用 ScriptComponentDescriptor 类的 AddElementProprety 方法传入对 HiddenField 的 DOM 元素的引用。

public IEnumerable<scriptdescriptor /> GetScriptDescriptors()
{
    ScriptComponentDescriptor scd = 
            new ScriptBehaviorDescriptor("Mbccs.WebControls.PersistentScrollPosition", 
            Control.ClientID);
    scd.AddElementProperty("storage", storage.ClientID);
    yield return scd;
}

在客户端,控件继承自 Sys.UI.Behavior 基类。在控件初始化时,它挂钩到两个事件:DIVscroll DOM 事件和 Sys.WebForms.PageRequestManager 类的 EndRequest 事件。EndRequest 事件是恢复滚动状态的时间,但我稍后会详细介绍。

initialize : function() {
        Mbccs.WebControls.PersistentScrollPosition.callBaseMethod(this, 'initialize');
        
        this._scrollDelegate = Function.createDelegate(this, this._onScroll);
        this._endRequestDelegate = Function.createDelegate(this, this._onEndRequest);
        
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_endRequest(this._endRequestDelegate);
        
        $addHandler(this.get_element(), 'scroll', this._scrollDelegate);
}

DIV 滚动时,x,y 滚动位置被序列化并存储在之前创建的 HiddenField 服务器端控件中。

_onScroll : function(e) {
        this._storage.value = 
             Sys.Serialization.JavaScriptSerializer.serialize(this._getScrollPosition());
    },

_getScrollPosition : function() {
        var el = this.get_element();
        
        if (el) {
            return {
                x: el.scrollLeft || 0,
                y: el.scrollTop || 0
            };
        }
    }

为了防止 null 值四处漂浮,如果 scrollLeftscrollTop 属性为 null,则 x,y 坐标会被强制转换为 0。

当“异步回发完成并且控件已返回到浏览器”时,会触发 EndRequest 事件,因此这是恢复滚动状态的最佳时机。

_onEndRequest : function(sender, args) {
        var o = null;        
        if(this._storage.value !== '')
            o = Sys.Serialization.JavaScriptSerializer.deserialize(this._storage.value);
            
        if (o) {
            var el = this.get_element();
            el.scrollLeft = o.x;
            el.scrollTop = o.y;
            this._storage.value = '';
        }
    }

dispose 方法通常不是任何特别感兴趣的地方,但值得注意的是,在对基类调用 dispose 之前,您需要取消挂钩 DIVscroll 事件。

dispose : function() {
        $removeHandler(this.get_element(), 'scroll', this._scrollDelegate);
        
        Mbccs.WebControls.PersistentScrollPosition.callBaseMethod(this, 'dispose');
        
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.remove_endRequest(this._endRequestDelegate);
        
        delete this._endRequestDelegate;
        delete this._scrollDelegate;
    }
© . All rights reserved.