首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

7.3  改善验证控件的用户体验

习惯于线性思维的程序员往往着眼于功能,但有时将目光转移到一些非功能性的角度,也许能获得事半功倍的效果。ASP.NET AJAX ControlToolkit中的ValidatorCalloutExtender正是一个绝好的例子。

图7-3  ValidatorCalloutExtender控件

ValidatorCalloutExtender控件并没有尝试去增强验证控件的功能,而是改善它的用户界面,从而获得令人惊讶的效果。

不过ValidatorCalloutExtender控件依赖于ASP.NET AJAX框架,而且它是一个控件扩展器,它对页面验证控件进行扩展,所以我们需要往页面中添加一个验证控件,再添加一个针对这个验证控件的ValidatorCalloutExtender控件才能得到这样的效果,因此使用起来并不方便。

接下来我们将实现一个必填项验证控件——CalloutReqiredFieldValidator,与RequiredFieldValidator不同的是,它显示错误消息的方式与应用了ValidatorCalloutExtender控件时相似。

7.3.1  ValidatorCallout客户端实现

和之前一样,我们先实现客户端行为。CalloutRequiredFieldValidator控件不需要重新设计验证算法,只是在原有验证过程的基础上增加显示丰富界面的功能。

var ValidatorCallout = function(validator){

    if(typeof(validator) == 'string')

        this._validator = document.getElementById(validator);

    else

        this._validator = validator;

 }

 ValidatorCallout.show = function(validator){

    validator.callout._divContainer.style.display = 'block';

 }

 ValidatorCallout.hide = function(validator){

    validator.callout._divContainer.style.display = 'none';

 }

 ValidatorCallout.zIndex = 999;

 ValidatorCallout.prototype = {

smallAlertPic :

'<%= WebResource("CustomValidators.alertsmall.gif")%>',

largeAlertPic :

'<%= WebResource("CustomValidators.alertlarge.gif")%>',

    closePic : '<%= WebResource("CustomValidators.close.gif")%>',

   

    initiate:function(){

        var divContainer = document.createElement("DIV");

        this._divContainer = divContainer;

        divContainer.style.position = 'absolute';

        divContainer.style.zIndex = ValidatorCallout.zIndex;

        ValidatorCallout.zIndex++;

        // UI被点击后显示在最前端,解决多个UI重叠的问题

        divContainer.onclick = function(){

            this.style.zIndex = ValidatorCallout.zIndex;

            ValidatorCallout.zIndex ++;

        };

        divContainer.style.cursor = 'default';

       

        var divCorner = document.createElement("DIV");

        divContainer.appendChild(divCorner);

        divCorner.style.width = 15;

        divCorner.style.height = 15;

        divCorner.style.position = 'relative';

        divCorner.style.top = 10;

        divCorner.style.left = 1;

        divCorner.style.borderTop = 'solid 1px black';

        divCorner.style.styleFloat = 'left';

        // 用多个元素拼出尖角效果

        for(var i = 14; i > 0; i--){

            var div = document.createElement("DIV");

            div.style.width = i;

            div.style.height = 1;

            div.style.styleFloat = 'right';

            div.style.clear = 'right';

            div.style.borderLeft = "solid 1px black";

            div.style.backgroundColor = 'lemonchiffon';

            divCorner.appendChild(div);

        }

        

        var divPanel = document.createElement('DIV');

        divContainer.appendChild(divPanel);

        divPanel.style.styleFloat = 'left';

        divPanel.style.border = 'solid 1px black';

        divPanel.style.padding = 12;

        divPanel.style.backgroundColor = 'lemonchiffon';

       

        var img = document.createElement("IMG");

        divPanel.appendChild(img);

        img.src = this.smallAlertPic;

        img.style.styleFloat = 'left';

        img.style.marginRight = 10;

        img.src = this.largeAlertPic;

       

        var h4 = document.createElement("H4");

        divPanel.appendChild(h4);

        h4.style.styleFloat = 'left';

        h4.style.clear = 'right';

        h4.innerText = 'Validation Message!';

        divPanel.appendChild(document.createElement('BR'));

       

        var span = document.createElement('SPAN');

        divPanel.appendChild(span);

        span.style.display = 'block';

        span.style.styleFloat = 'left';

        span.style.clear = 'right';

        span.appendChild(document.createTextNode(

                            this._validator.errormessage));

       

        var close = document.createElement('IMG');

        divPanel.appendChild(close);

        close.src = this.closePic;

        close.style.position = 'absolute';

        close.style.top = 2;

        close.style.right = 2;

        close.onclick = function(){divContainer.style.display = 'none';};

        document.body.insertBefore(

                            divContainer,document.body.children[0]);

       

        // 将UI定位在验证控件的旁边

        var position =  WebForm_GetElementPosition(this._validator);

        position.x += this._validator.offsetWidth;

        WebForm_SetElementX(divContainer,position.x);

        WebForm_SetElementY(divContainer,position.y);

       

        divContainer.style.display = 'none';

       

        this._validator.callout = this; 

        

        //验证控件的验证方法上附加显示UI的功能

        if(typeof(this._validator.evaluationfunction) ==  'String'){

            this._validator.evaluationfunction =

                        eval(this._validator.evaluationfunction);

        }  

        var evaluationfunction = this._validator.evaluationfunction;

        this._validator.evaluationfunction = function(val){

            if(!evaluationfunction(val)){

                ValidatorCallout.show(val);

                return false;

            }

            else{

                ValidatorCallout.hide(val);

                return true;

            }

        };

    }

};

同样的,我们将CalloutValidator实现为一个JavaScript组件,只需传给它一个验证控件的ID,并调用它的initiate(),即可实现不一样的界面。

CalloutValidator的原型(prototype)中定义了几个内嵌资源图片,它们将用于组成Callout UI,如图7-4所示。

图7-4  ValidatorCallout外观

特别需要注意的是ValidatorCallout左边的小尖角,正是由于这个小小的尖角让整个UI活泼了许多,不过这个尖角并不是一个图片,而是由十多个宽度不一的DIV组成的,这些DIV都停靠在父DIV的右边,每个DIV都有黑色的左边框,高1px。由于每个DIV宽度相差一个像素,所以它们的左边框就形成了一条斜边。此外,它们的父容器DIV向右偏移1px,这样就遮住了右边主DIV的黑色边框,使尖角与主DIV融为一体。

整个UI都是在initiate()方法中动态生成的,为了解决多个UI相互重叠使得处于下方的UI不能完整显示的问题,我们还编写了UI的oncLick事件处理函数,其效果如图7-5所示。

divContainer.style.zIndex = ValidatorCallout.zIndex;

ValidatorCallout.zIndex++;

// UI被点击后显示在最前端,解决多个UI重叠的问题

divContainer.onclick = function(){

    this.style.zIndex = ValidatorCallout.zIndex;

    ValidatorCallout.zIndex ++;

};

图7-5  将被点击的UI置于前端

7.3.2  ValidatorCallout服务端实现

由于客服端功能设计得十分内聚,所以服务端的实现就非常简单了。

[assembly:WebResource("CustomValidators.alertlarge.gif","image/gif")]

[assembly: WebResource("CustomValidators.alertsmall.gif", "image/gif")]

[assembly: WebResource("CustomValidators.close.gif", "image/gif")]

[assembly: WebResource("CustomValidators.ValidatorCallout.js", "application /x-javascript",PerformSubstitution=true)]

namespace CustomValidators

{

[ToolboxData("<{0}:CalloutRequiredFieldValidator runat=\"server\"

                        ErrorMessage=\"CalloutRequiredFieldValidator\">

                        </{0}:CalloutRequiredFieldValidator>")]

    public class CalloutRequiredFieldValidator:RequiredFieldValidator

    {

        protected override void OnPreRender(EventArgs e)

        {           

            base.OnPreRender(e);

            if (RenderUplevel)

            {

                Page.ClientScript.RegisterClientScriptResource(

                        typeof(CalloutRequiredFieldValidator),

                        "CustomValidators.ValidatorCallout.js");

                Page.ClientScript.RegisterStartupScript(

                        typeof(CalloutRequiredFieldValidator),

                        this.ClientID + "Callout",

                    "new ValidatorCallout('"

                        +this.ClientID+"').initiate();\r\n", true);

            }

        }

    }

}

CalloutRequiredFieldValidator继承自RequiredFieldValidator,仅重写了它的OnPreRender()方法。OnPreRender()方法中使用的RenderUplevel属性由BaseValidator基类提供,通过调用base.OnPreRender()方法,RenderUplevel可以指示访问浏览器是否支持客户端验证。

此外,我们在页面中注册了必需的客户端脚本文件,并生成初始化ValidatorCallout客户端组件的脚本。

7.3.3  ValidatorCalloutExtender

ASP.NET AJAX ControlTookit 中的Extender控件不修改现有控件而扩展现有控件的方式很有借鉴意义,它体现的正是OOP的“开放-关闭”原则。不过ControlToolkit中的ValiatorCalloutExtender需要为每个需要扩展的验证控件添加一个Extender,并不便于大量重构现有页面。而刚完成的ValidatorCallout组件非常易于与现有验证控件框架结合,所以可以实现一个扩展控件,添加一个就可扩展页面中的所有验证控件,如图7-6所示。

图7-6  ValidatorCalloutExtender扩展页面中的所有验证控件

接下来要实现的ValidatorCalloutExtender控件非常简单,它在页面中注册一段脚本,为Page_Validators数组中的每个验证控件初始化一个ValidatorCallout组件。

[Designer(typeof(ValidatorCalloutExtenderDesigner))]

public class ValidatorCalloutExtender:Control

{

    protected override void OnPreRender(EventArgs e)

    {

        // 检测访问浏览器是否支持脚本功能

        if (Page.Request.Browser.MSDomVersion.Major > 0

            && Page.Request.Browser.EcmaScriptVersion.Major > 0)

        {

            Page.ClientScript.RegisterClientScriptResource(

                    typeof(ValidatorCalloutExtender),

                "CustomValidators.ValidatorCallout.js");

            if (!Page.ClientScript.IsStartupScriptRegistered(

                                            "ValidatorCalloutExtender"))

            {

                Page.ClientScript.RegisterStartupScript(

                    typeof(ValidatorCalloutExtender),

                      "ValidatorCalloutExtender",

                      "for(var i = 0 ; i < Page_Validators.length; i++)

                    {new ValidatorCallout(Page_Validators[i])

                    .initiate();}\r\n", true);

            }

        }

       

    }

    public override void RenderControl(HtmlTextWriter writer)

    {

    }

}

public class ValidatorCalloutExtenderDesigner : ControlDesigner

{

    public override string GetDesignTimeHtml()

    {

        return CreatePlaceHolderDesignTimeHtml(

                            "ValidatorCalloutExtender");

    }

}

与数据源控件等控件一样,ValidatorCalloutExtender控件并不生成和用户交互的客户端控件,所以从严格意义上讲,它其实不是控件,而是一个组件,所以我们为它提供了ValidatorCalloutExtenderDesigne设计器,返回只有占位块的设计时HTML代码,如图7-7所示。

 

图7-7  ValidatorCalloutExtender设计时效果

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论