基于CommunityServer 2.0二次开发之登录控件

文/basilwang  出处/博客园

在CS 2.0下,使用了大量的Ajax来提高用户体验,比如AjaxPager来实现无刷新翻页,以后有时间会把AjaxPager讨论一下。

大家可能知道,在CS里面,登录是专门放到一个页面来处理的,当点击首页时,跳转到登录页面。这样做的好处是逻辑比较清楚,代码比较简单。但缺点是每次登录完后需要重新跳转到首页,增加了用户的等待时间。而我们在二次开发的过程中,很多时候希望在首页上直接有登录的区域,类似很多门户网站的操作,但是又不希望每次登录都PostBack,这样的话采用了Ajax。下面是我开发的登录控件的过程,另外由于我现在工作的环境主要还是framework 1.1,所以开发的这个登录控件也可以在1.1环境下使用。

先说一下开发的思路,由于原来CS的登录也是做成了控件的形式,通过AnonymousTemplate和LoggedInTemplate模版来配置登录时和登录后的页面,在后台完成相应的代码,也就是和Asp.net 2.0里的登录控件差不多(2.0里面简单看了看,可能比较类似)。因此改造登录控件时,我也希望延用这种方式,再把Ajax加上去。

其实在之前我也写了一个基于CS的登录控件,但是感觉不够模块化,那个控件采用了Ajax的Anthem第三方开源控件,但是正如上一篇文章CommunityServer 2.0中的Ajax和Anthem比较提到的,由于只有标识为UpdateAfterCallBack=true的控件才能够完成无刷新更新,如果页面的控件比较多,而且Asp.net控件和Anthem控件堆到一起时,代码非常的乱;另外Anthem的脚本会自动生成到aspx页中,个人感觉不便于缓存,而且如果首页上只有登录控件使用Anthem,也会生成大量的Anthem脚本文件,很是不爽。而CS2.0的Ajax,给人感觉代码比较简洁,有的时候也比较灵活。因此采用了CS 2.0的Ajax来完成,重构后的登录控件和页面耦合度更低,可以放置到各个页面而且不需要修改代码,另外可以通过Template来更改布局。

下面来看一下代码:
AjaxManager.cs这是CommunityServer中用来封装Ajax的一些代码
global.js CommunityServer中实现Ajax_CallBack的脚本
TemplateWebControl CS中用来实现皮肤调用的基类,在CS中只要有类继承此类,就需要有对应的皮肤
Login.cs 继承了TemplateWebControl,相当于一个容器
LoginView.cs 这里为了方便,直接继承了Panel,但是参考原有CS的实现,使用了ITemplate
AnonymousUserCtrlComplex.cs 匿名用户看到的内容
RegisteredUserControl.cs 登录用户看到的内容
这里着重看一下LoginView的部分代码
[PersistChildren(false), ParseChildren(true)]
    public class LoginView : Panel,INamingContainer
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit (e);
       
            AjaxManager.Register(this,"Login");
        }

        public LoginView()
        {

        }
        [AjaxMethod(IncludeControlValuesWithCallBack=true)]
        public string ValidUser(string user,string password)
        {
            //CommunityServer.Components.User userToLogin = new CommunityServer.Components.User();
            string redirectUrl = null;
           
            if(user=="admin" && password=="admin")
            {
           
                FormsAuthentication.SetAuthCookie(user, false);
           
            }
            StringWriter stringWriter = new StringWriter();
            HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);           


            this.Controls.Clear();
            this.ChildControlsCreated=false;
            this.Page.DataBind();
            this.Render(htmlWriter);

           

            return stringWriter.ToString();
        }
        public override void DataBind()
        {
            this.EnsureChildControls();
            base.DataBind ();
        }
        protected override void CreateChildControls()
        {
            this.Controls.Clear();
            this.ChildControlsCreated=false;
            ITemplate template  = null;

            if(!Context.User.Identity.IsAuthenticated)
            {
                template = AnonymousTemplate;
            }
            else
            {
                template = LoggedInTemplate;               
            }

            if(template != null)
            {
                //basilwang 2008-02-26 没有采用原有CS增加Control的方式,而保持AnonymousTemplate的Parent为LoginView
                //Control cntrl = new Control();
                //template.InstantiateIn(cntrl);
                template.InstantiateIn(this);
                //this.Controls.Add(cntrl);
            }
           
        }
       
        [
        Browsable( false ),
        DefaultValue( null ),
            Description( "TODO SkinTemplate Description" ),
        PersistenceMode( PersistenceMode.InnerProperty ),
        ]
        public ITemplate AnonymousTemplate
        {
            get {return _anonymousTemplate;}
            set {_anonymousTemplate = value;}
        }
        private ITemplate _anonymousTemplate;

        [
        Browsable( false ),
        DefaultValue( null ),
        Description( "TODO SkinTemplate Description" ),
        PersistenceMode( PersistenceMode.InnerProperty ),
        ]
        public ITemplate LoggedInTemplate
        {
            get {return _loggedInTemplate;}
            set {_loggedInTemplate = value;}
        }
        private ITemplate _loggedInTemplate;

    }

注意AjaxManager.Register的注册时间,是在生命周期的最开始Init完成的
另外对于CallBack的函数一定要加上[AjaxMethod(IncludeControlValuesWithCallBack=true)]
那么对于回调生成的html,这里我向老赵学习,尽量通过framework生成规整的html,当然这里也费了我很多的时间
由于采用了模版ITemplate,而模版在AddParsedSubObject的时候只是add到Control集合当中,注意此时并没有调用模版的CreateChildControls方法,那么什么时候这些方法会被调用呢,我开始一直认为是在Page的PreRender,但是通过Reflector发现Page的PreRenderRecursiveInternal在执行完this.EnsureChildControls()后(此时还没有递归调用他的子控件的PreRender),紧接着处理 this.OnPreRender(EventArgs.Empty);但是由于我们的Ajax方案是在PreRender时候即返回了结构,所以总是得不到Template的值。后来我使用了DataBind(建议大家在使用模版ITemplate时,别忘了DataBind这个好用的方法),因为DataBind()有一个递归EnsureChildControls()方法,可以帮助我们把所有的子控件都给创建出来,最后的事情就比较简单了,Render出来就可以了。

另外在WebForm1.aspx页面上一定要加

{
    //base.VerifyRenderingInServerForm(control);
}

防止CallBack时框架验证是否包含在服务器端Form中,这也是我觉得不爽的地方

 感谢原创者的辛勤劳动,希望对您有所帮助,转载请注明原出处。
 您可能对 [Ajax] 的这些文章也感兴趣:

利用AJAX J2EE开发组织机构管理系统
将java和Ajax结合在一起
理解Ajax及其工作原理,构建网站的一种有效方法
使用javascript和Ajax发出异步请求
Ajax Control Toolkit for .NET3.5 SP1 Released
Ajax.net ModalPopup控件
在 AJAX 开发中集成数据库技术
IE8 Beta2 AJAX 的更新(一)(IE8 Beta2 Hand-on Lab)
DWR让Ajax如此简单
使用ASP.NET AJAX的注意事项