乞丐中的霸主(ぃ枫.net)(天行健,君子以自强不息;地势坤,君子以厚德载物! -《周易》)QQ讨论群(交流EVC++,VC++,USB驱动程序开发,上下机位通讯,无线网络通讯技术,Mobile开发)帮主QQ号:414885058

C#,C++ 学习

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

前言:一些介绍

动态加载控件

视图状态的保存

重构页面/控件

初始化页面/控件/IsPostBack

了解一下控件的生命周期。

1Instantiate
2Initialize
3Begin Tracking View State
4Load View State (postback only)
5Load Postback Data (postback only)
6Load
7Raise Changed Events (postback only, optional)
8Raise Postback Events (postback only, optional)
9PreRender
10SaveViewState
11Render
12Unload
13Dispose

视图状态ViewStateasp.net提供的一个集合,用来保存页面上控件的状态。

视图状态的恢复asp.net会在某个特定的时刻(Load View State)把保留在ViewState集合中的数据恢复到相应的控件中(根据该控件的id

控件状态的追赶论:在contrlParant(已经到状态n,n肯定就在上述13个中的一个)中加入一个contrlChild,那么controlChild的状态在controlParent.Control.Add(contrlChild)之后就会立即经历n-1个状态,到达和contrlParant同步的状态。

-------------------------

这是一个困扰了我近两年的问题。刚开始是无法恢复状态;后来想办法恢复了状态但是无法避免重复的初始化(浪费效率);再后来解决了,但是认识比较浅。今天终于有所悟。

问题的提出:在一个aspx页面上,根据传递的参数来加载不同的UserControl。保证UserControl的顺利执行还有效率:

今天在网上搜索,得到这样的文章http://www.cnblogs.com/alex.zhang/archive/2005/03/31/129427.html。里面写得洋洋洒洒。结合我自己的应用,简单的说一下。

问题在哪里?

比如你在一个Page_Load事件里面这样写

 

  protected void Page_Load(object sender, EventArgs e)
    
{
        
if (!IsPostBack)
        

            Button btn
=new Button();
            btn.Click
+=new EventHandler(btn_Click);
            btn.Text 
= "Click Me";
            
this.PlaceHolder1.Controls.Add(btn);
        }

    }

 

    
void btn_Click(object sender, EventArgs e)
    
{
          (sender 
as Button).Text = "Got U";
    }

那么,你下次回发这个页面之后(点击按钮之后),这个按钮就消失了。因为没有重构(控件只是生成了一次,因为在if (!IsPostBack)里面)。

这部分,需要了解不少关于ViewStateasp.net的生命周期的知识。简单的说,asp.net根据页面上的控件树来恢复状态,但是由于控件是在程序中生成,并且只生成了一次,所以在叶面提交的时候asp.net没有找到该控件的id,自然也不会重构他,页面上也就显示不出来这个控减了。

如果这样就可以重构:去掉if (!IsPostBack),每次都加载

   

protected void Page_Load(object sender, EventArgs e)
    
{
        Button btn 
= new Button();
        btn.Click 
+= new EventHandler(btn_Click);
        btn.Text 
= "Click Me";
      
        
this.PlaceHolder1.Controls.Add(btn);
   
  }

但是问题依然有,它的事件你可能捕捉不到(这个例子太简单,如果控件数量多,他们的id的生成就会很意外,导致找不到一样的id)。对象的状态的恢复是根据控件的id来进行的。如果id不确定(自动生成的id),那么它也就无法恢复状态。启动叶面的trace 可以看到这棵控件树。大体这样:

__Page

ASP.agent_default_aspx

2411

0

0

    ctl02

System.Web.UI.LiteralControl

148

0

0

    ctl00

System.Web.UI.HtmlControls.HtmlHead

46

0

0

        ctl01

System.Web.UI.HtmlControls.HtmlTitle

33

0

0

    ctl03

System.Web.UI.LiteralControl

14

0

0

    form1

System.Web.UI.HtmlControls.HtmlForm

2183

0

0

        ctl04

System.Web.UI.LiteralControl

10

0

0

        ScriptManager1

Microsoft.Web.UI.ScriptManager

224

0

0

        ctl05

System.Web.UI.LiteralControl

29

0

0

        PlaceHolder1

System.Web.UI.WebControls.PlaceHolder

64

0

0

            btn_t

System.Web.UI.WebControls.Button

64

0

0

        ctl06

System.Web.UI.LiteralControl

14

0

0

        Button1

System.Web.UI.WebControls.Button

76

0

0

        ctl07

System.Web.UI.LiteralControl

22

0

0

    ctl08

System.Web.UI.LiteralControl

20

0

0

如果一下子生成大量控件的时候,由于id的不确定,两次生成的控件id可能不一样,所以也无法正确恢复状态。

所以我们需要确定的id。比如这样:

        Button btn = new Button();
        btn.Click 
+= new EventHandler(btn_Click);
        btn.Text 
= "Click Me";
        btn.ID 
="btn_t";
        
this.PlaceHolder1.Controls.Add(btn);


这样呢?看上去可以了。但是问题是,你做了重复的初始化工作(每次Page_Load都在初始化这个控件)。如果我们把应用放到UserControl上,我们动态加载的是一个UserControl,而这个Control的初始化事件又非常耗时

比如:

public partial class Agent_uc1 : System.Web.UI.UserControl
{
    
protected void Page_Load(object sender, EventArgs e)
    
{
        System.Threading.Thread.Sleep(
20000);
    }

}


那么,这个实现就太没效率了,付出了高昂的代价。

怎么解决呢?

先看我引用的文章的一点描述,然后再说我的实现

道行限制,也没仔细看,所以不敢说看懂了多少。

文章主要使用那个“控件状态追赶论”来解释的。先加入控件(主要是id配对),然后就可以被正确加载(追赶过程中有一步会根据控件id来恢复视图状态)。

我的实现就是这样的:

//Page的基类
public class BasePattern:Page
{
    
protected override void OnInit(EventArgs e)
    
{
        
string path= defaultLoadMoudle;//first, load default
         if(path!=null)
            
this.RebuildControl(path);
        
//
        base.OnInit(e);
    }

    
private BaseView RebuildControl(string path)
    
{
        Control ctl 
= this.LoadControl("~/Module/" + path);
        
if (ctl != null)
        
{
            ctl.ID 
= path;
            PlaceHolder container 
= this.Master.FindControl("cph_view").FindControl("PlaceHolder1"as PlaceHolder;
            container.Controls.Clear();
            container.Controls.Add(ctl);
  
        }

        
return ctl as BaseView;
    }

    
#endregion
 
    
public void LoadModule(string path)
    
{
        BaseView view 
= RebuildControl(path);
        view.BindEntity();
    }


//UserControl的基类

public class BaseView:UserControl
{
    
/// <summary>
    
/// init the control
    
/// </summary>

    virtual public void BindEntity()
    
{
 
           //doing sth here,binding or init
    }

    
public BasePattern ParentPattern
    
{
        
get return this.Page as BasePattern; }
    }


//调用的时候

    protected void lbt_summary_Click(object sender, EventArgs e)
    
{
        
//show sumary and list
         string path = "ToDoList/ToDoList.ascx";
        
this. ParentPattern.LoadModule(path);
    }


1:必须每次都执行创建该控件的工作。其实主要是建立这个控件的名称,恢复控件树

   protected override void OnInit(EventArgs e)

   这个事件比Page_Load靠前。

2:保证id一样

 ctl.ID = path;//让同一个控件的id唯一。

3:区分重建和执行

所以在Page德类里面我用的是两个函数Rebuild()和LoadModule(),目的就是区分这两个调用。内部的重建只是使用Rebuild,只是建立一个控件id的过程,外部调用的时候,就需要调用控件的初始化函数了

结论就是

1:在特定的时刻加入该控件的定义。至少在Page_Load以前,我用的Page_Init。晚了就执行不了了

2:该控件的id必须一致。因为状态的恢复是根据控件id来完成的。

3Rebuild的时候一定不要调用子控件的初始化的函数,这样会浪费时间。

问题:可不可以通过设置UserControlIsPostBack属性来达到一种和Page类似的处理方式呢?这样在UserControl里面就可以使用if(IsPostBack)来做一些数据初始化了。

我记得我曾经看过一篇文章,可以在某个事件中设置IsPostBack属性,但是现在怎么也找不到这篇文章了,可惜得很。

posted on 2007-10-30 10:52  arvin2012  阅读(298)  评论(0)    收藏  举报