代码改变世界

Tip:Modal UpdateProgress的轻量级解决方案

2007-03-22 08:50 by Jeffrey Zhao, ... 阅读, ... 评论, 收藏, 编辑

又被问了这样一个问题:UpdateProgress如何像ModalPopupExtender那样的效果呢?类似的问题有:如何在UpdatePanel里使用ModalPopupExtender呢?

以前被问到这个问题的时候,我一般是这么回复的:UpdateProgress里可以放置任意HTML,您只要保证UpdateProgress里的HTML能够是Modal的就可以了,并不需要用到ModalPopupExtender。

说实话,我的确不知道如何将ModalPopupExtender用于UpdateProgress之上。因为ModalPopupBehavior需要一个Element作为触发器,用于弹出ModalDialog。UpdateProgress并没有这么一个触发器,虽然我们可以调用Behvaior的hide和show方法来控制ModalDialog,可是UpdateProgress又没有任何事件等等,我们又该在什么时候调用这些方法呢?当然,我们可以改写一下UpdateProgress,为它放出一些事件,这样我们就可以响应这些事件来进行一些操作了。不过这种做法“trick”的成分感觉会大于“solution”,我更希望通过一种“正统”的方式来解决这个问题。

其实我之前的回答“编写合适的HTML内容”是个非常“正统”的做法,在我有想法写这篇文章之前不久,我还认为这个问题并不难以解决。但是当我仔细想了UpdateProgress里填充的内容,发现它并不那么简单。而面临的主要难点,更确切地说应该是“麻烦点”,有如下两个:

  • 如何在跨浏览器的情况下得到Modal背景的尺寸?
  • 如何在滚动条移动时保持PopupDialog在浏览器中的位置?

这两个问题都不难,而且事实上在ModalPopupBehavior中都可以找到解决方案,但是我还是想自己写一些代码。不过写着写着,调试来调试去总是不那么理想,其实主要还是第一个跨浏览器的问题。最后还是翻了翻ModalPopupBehavior的代码,找到了它的相关实现,剥离出来由我们来使用。决定这么做之后,没花多少时间就解决问题了。

哎,作为一个开发人员,做事还是要谦虚谨慎啊,一定要多多吸取前人的经验来提高自己。

最后得到的结果,是一个轻量级的UpdateProgress实现。为什么说是“轻量级”的呢?因为编写的代码少,但是代码本身不具有很高的通用性,在使用时还需要根据具体的情况来作一些修改。

首先,我们先在页面上随意放一个UpdatePanel和UpdateProgress,里面的内容都非常简单,如下:

<div style="height: 2000px;">
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <%= DateTime.Now %>
            <asp:Button ID="Button1" runat="server" Text="Button"
	        OnClick="Button1_Click" />
        </ContentTemplate>
    </asp:UpdatePanel>
</div>

<asp:UpdateProgress ID="UpdateProgress1" runat="server">
    <ProgressTemplate>
        <div id="modalBackground"></div>
        <div id="animationDialog">
            <img src="animated_loading.gif" alt="Loading" />
            Working on your request...
        </div>
    </ProgressTemplate>
</asp:UpdateProgress>

 

在UpdateProgres中,我们放置一个id为modalBackground的div作为Modal背景,还有一个id为animationDialog的div,这才是真正用作UpdateProgress提示的内容。

然后,我们在页面上先准备一些稳定的CSS Style,如下:

html
{
    font-zize : 10pt;
    font-family : Verdana;
}
#modalBackground
{
    background-color : gray;
    filter : alpha(opacity=70);
    opacity : 0.7;
    position : absolute;
    top : 0px;
    left : 0px;
}
#animationDialog
{
    position : absolute;
    border : solid 1px black;
    color : Black;
    background-color : #ffffae;
    font-family : Arial;
    font-size : 8pt;
    font-weight : bold;
    line-height : 30px;
    height : 30px;
    padding-left : 5px;
    padding-right : 5px;
}

 

接着就是最为关键的JavaScript代码了,它有两个作用,一是用于调节Modal背景的尺寸,二是调整animationDialog的位置(在这里我就将其固定在左上角),如下:

function getClientBounds()
{
    var clientWidth;
    var clientHeight;
    switch(Sys.Browser.agent) {
        case Sys.Browser.InternetExplorer:
            clientWidth = document.documentElement.clientWidth;
            clientHeight = document.documentElement.clientHeight;
            break;
        case Sys.Browser.Safari:
            clientWidth = window.innerWidth;
            clientHeight = window.innerHeight;
            break;
        case Sys.Browser.Opera:
            clientWidth = Math.min(window.innerWidth, 
                document.body.clientWidth);
            clientHeight = Math.min(window.innerHeight,
                document.body.clientHeight);
            break;
        default:  // Sys.Browser.Firefox, etc.
            clientWidth = Math.min(window.innerWidth,
                document.documentElement.clientWidth);
            clientHeight = Math.min(window.innerHeight,
                document.documentElement.clientHeight);
            break;
    }

    return new Sys.UI.Bounds(0, 0, clientWidth, clientHeight);
}

function resizeElements()
{
    var clientBounds = getClientBounds();
    var clientWidth = clientBounds.width;
    var clientHeight = clientBounds.height;

    var bg = $get("modalBackground");    
    bg.style.width = Math.max(
            Math.max(document.documentElement.scrollWidth, document.body.scrollWidth),
	    clientWidth) + 'px';
    bg.style.height = Math.max(
            Math.max(document.documentElement.scrollHeight, document.body.scrollHeight),
	    clientHeight) + 'px';

    var scrollLeft = (document.documentElement.scrollLeft ?
            document.documentElement.scrollLeft : document.body.scrollLeft);
    var scrollTop = (document.documentElement.scrollTop ?
            document.documentElement.scrollTop : document.body.scrollTop);    
    var dialog = $get("animationDialog");
    dialog.style.left = scrollLeft + "px";
    dialog.style.top = scrollTop + "px";
}

$addHandler(window, "scroll", resizeElements);
$addHandler(window, "resize", resizeElements);
resizeElements();

 

resizeElements方法的作用就是调节元素尺寸和位置。但是从获得Modal背景尺寸的工作中就可以发现,世界上存在那么多不同的浏览器实现有时真的是一件让人痛苦的事情。getClientBounds方法的作用是获得浏览器可视区域的大小,这段实现是从AjaxControlTookit中的Common.js中提取出来的。然后对元素的位置和大小进行设置的方式相信大家能够轻松地看明白,仔细想想也就可以理解DOM元素各个属性的具体含义了。最后,我们会在window的resize和scroll事件中调整DOM元素的属性——这是显而易见的。

这个示例的效果如下:

sample

 

以上就是Modal UpdateProgress实现方式了,朋友们可以尝试着移动滚动条,此时UpdateProgress会停留在左上角——可惜延迟是不可避免的。

嘿,既然这个是“轻量级”的实现,那么“重量级”的做法是什么呢?其实我正在写一个ModalUpdateProgress控件,算是UpdateProgress的扩展吧。这次可真要用到ModalPoupExtender了,其后果就是可能一个小小的功能就需要引入较多脚本代码。这其实是一个AjaxControlToolkit长久以来的一个问题。大家要不和我一起想想,该如何解决或者缓解这个问题呢?当然,不限于所谓的ModalUpdateProgress或者ModalPopupBehavior,这是为整个AjaxControlTookit要求的。

 

代码下载

点击这里浏览使用效果。

使用Live Messenger联系我