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

高级AJAX列表框组件 v0.3

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2007 年 6 月 30 日

CPOL

4分钟阅读

viewsIcon

55431

downloadIcon

397

强制浏览器兼容性以支持水平滚动和滚动状态的保存。

ListBoxComponent0.3.gif

引言

我上一篇文章中,我们更新了我们的ASP.NET AJAX启用的列表框控件,并将客户端滚动状态的保存与水平滚动分离开来,以便两者可以单独配置。在本文中,我们将在此代码的基础上进一步强制跨浏览器功能。

背景

我们已经走了很长一段路,但我们的列表框控件还有更多功能要添加。不过,最大的功能我们会留到下一篇文章再处理,我来告诉您为什么。上篇文章遗留的主要问题是,只有当HorizontalScrollEnabled设置为true时,滚动状态才能在IE6中正确保存。这是因为IE6不会执行列表框的onscroll事件,并且始终返回0作为列表框的scrollTop值。此外,即使解决了这两个问题,它仍然无法正常工作,因为在IE6中设置列表框的scrollTop根本不起作用。就我个人而言,要求HorizontalScrollEnabledtrue才能在像IE6这样常见的浏览器中实现预期功能是不可接受的……尤其是我们可以通过代码更改来解决它。

我们希望尽可能少地使用“ hack ”,但同时,优秀的程序员有时确实必须编写 hack……而我个人在它们有正当理由时感觉会好一些。在这种情况下,IE6的行为与Firefox(以及IE7)的行为差异很大,我可以为编写浏览器 hack 辩护。我们的列表框的scrollTop属性在IE6中完全无效。但是,我们容器DIVscrollTop却完全有效。即使HorizontalScrollEnabledfalse,我们也可以通过使用IE6的DIV容器的滚动条来实现我们想要的结果。

如果你忍不住,就把它放在服务器上

我无法辩护的是任何客户端浏览器嗅探代码。我们可以从PageRequest.Browser对象中获取服务器上一致且有用的浏览器信息。棘手的部分是如何重构代码,以便控件根据HorizontalScrollEnabledScrollStateEnabled属性在所有目标浏览器中正确呈现。在上一篇文章中,我们从0.1版本开始的单一HorizontalScrollEnabled设置中提取了ScrollStateEnabled属性。我们在这里要做的事情没有什么不同……我们需要从HorizontalScrollEnabled属性中分离出一个RequiresScrollContainer属性。同样,让我们先添加这个属性。

protected virtual bool RequiresContainerScroll
{
    get
    {
        if (HorizontalScrollEnabled)
            return true;

        else if (ScrollStateEnabled
            && Page.Request.Browser.Browser.Equals("IE")
            && Page.Request.Browser.MajorVersion < 7)
            return true;

            // Opera exhibits the same behavior as IE6 when

            // scrolling inside and outside of a DIV container

        else if (ScrollStateEnabled 
            && Page.Request.Browser.Browser.Equals("Opera"))
            return true;

        return false;
    }
}

我们在这里的意思是,列表框是否需要由DIV容器处理滚动,这不仅仅取决于HorizontalScrollEnabled属性。当然,如果HorizontalScrollEnabledtrue,那么我们当然需要由DIV来处理水平滚动。但是,即使它为false,当ScrollStateEnabledtrue时,我们仍然需要DIV来处理IE6(以及,结果证明,Opera)的滚动。

使用这个新属性,我们可以相当容易地重构渲染代码。我们不再仅仅在HorizontalScrollEnabledtrue时将某些样式属性委托给容器,现在当RequiresContainerScrolltrue时,我们希望将它们委托过去。

protected virtual void AddContainerAttributesToRender(HtmlTextWriter writer)
{
    // the container now depends on 3 different property values

    if (this.HorizontalScrollEnabled || this.ScrollStateEnabled)
    {
        // add required container attributes

        writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID
            + ContainerClientIdSuffix);

        // add conditional container attributes

        if (this.HorizontalScrollEnabled)
        {
            writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "auto");
        }
        else if (this.RequiresContainerScroll)
        {
            // Opera doesn't support overflow-x or overflow-y

            writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "auto");
            writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowX, "hidden");
        }

        if (this.RequiresContainerScroll)
        {
            writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
                this.Width.ToString());

            // add other optional container attributes

            // move style declarations from the Style attribute 

            // into the DIV container.

        }
    }
}

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
    if (RequiresContainerScroll)
    {
        // the code inside this if block stays the same.

        // only the condition is changed.

    }
    else
    {
        base.AddAttributesToRender(writer);
    }
}

由于Opera不支持overflow-xoverflow-y,即使HorizontalScrollEnabledfalse,它也会显示水平滚动条。为了避免处理这个问题,我们就假装Opera是一个很棒的、高级的浏览器,它在我们不帮助的情况下也能显示水平滚动条:P

将服务器控件属性传递给客户端控件

现在,我们需要告诉客户端脚本控件是否需要容器滚动。这部分现在应该很容易了。

protected virtual IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
    ScriptControlDescriptor descriptor = new ScriptControlDescriptor(
        "DanLudwig.Controls.Client.ListBox", this.ClientID);
    descriptor.AddProperty("requiresContainerScroll", 
        this.RequiresContainerScroll);
    descriptor.AddProperty("scrollStateEnabled", this.ScrollStateEnabled);
    descriptor.AddProperty("horizontalScrollEnabled", 
        this.HorizontalScrollEnabled);
    descriptor.AddProperty("scrollTop", this.ScrollTop);
    descriptor.AddProperty("scrollLeft", this.ScrollLeft);
    return new ScriptDescriptor[] { descriptor };
}
// 2.)

// Define the client control's class

//

DanLudwig.Controls.Client.ListBox = function(element)
{
    // initialize base (Sys.UI.Control)

    DanLudwig.Controls.Client.ListBox.initializeBase(this, [element]);

    // declare fields for use by properties

    this._requiresContainerScroll = null;
    this._scrollStateEnabled = null;
    this._horizontalScrollEnabled = null;
    this._scrollTop = null;
    this._scrollLeft = null;
}
    // 3d) 

    // Define the property get and set methods.

    //    

    set_requiresContainerScroll : function(value) 
    {
        if (this._requiresContainerScroll !== value)
        {
            this._requiresContainerScroll = value;
            this.raisePropertyChanged('_requiresContainerScroll');
        }
    }
,
    get_requiresContainerScroll : function()
    {
        return this._requiresContainerScroll;
    }
,

这就是我们将浏览器 hack 保留在服务器端的方式。

在客户端控件中使用服务器控件属性

现在,我们必须在客户端代码中使用此新属性来注册正确的事件、初始化UI、存储正确的滚动状态,然后在回发后恢复它。这一切都涉及到将某些this.get_horizontalScrollEnabled()的实例替换为this.get_requiresContainerScroll(),就像我们在服务器代码中所做的那样。以下是它的样子

    _initializeEvents : function()
    {
        // when horizontal scroll is enabled, use 3 zivros events

        if (this.get_requiresContainerScroll())
        {
            // same code that previously fell under

            // if (this.get_horizontalScrollEnabled())

        }
        
        // the rest of this method stays the same

    }
,
    _initializeUI : function()
    {
        var listBox = this.get_element();
        var container = this.get_elementContainer();
        
        if (this.get_requiresContainerScroll())
        {
            // same code that previously fell under

            // if (this.get_horizontalScrollEnabled())

        }
        
        if (this.get_scrollStateEnabled())
        {
            this._restoreScrollState();
        }
        
        if (this.get_requiresContainerScroll())
        {
            // same code that previously fell under

            // if (this.get_horizontalScrollEnabled())

        }
    }
,
    _restoreScrollState : function()
    {
        var scrollingElement = this.get_elementContainer();
        if (!this.get_requiresContainerScroll())
            scrollingElement = this.get_element();
        
        scrollingElement.scrollTop = this.get_scrollTop();
        scrollingElement.scrollLeft = this.get_scrollLeft();
    }
,
    // return the client scroll state data that will go in the hidden field

    get_scrollState : function()
    {
        var scrollingElement = this.get_elementContainer();
        if (!this.get_requiresContainerScroll())
            scrollingElement = this.get_element();
        
        return scrollingElement.scrollTop + ':' + scrollingElement.scrollLeft;
    }
,

……好了。现在,当禁用水平滚动并启用滚动状态保存时,我们可以同时支持IE6和Opera(到目前为止只在Opera 9.21上测试过)。同样,唯一的怪癖是Opera即使在禁用水平滚动时也会显示水平滚动条。据我所知,这是支持该浏览器所必需的。好消息是,如果Opera将来决定支持overflow-xoverflow-y CSS属性,此代码也应该能够适应。

© . All rights reserved.