.net2.0数据绑定及动态加载控件

  <asp:Button Text="<%# bb %>" ID="ssss" runat="server" Width="162px" />

一:

前言:一些介绍
动态加载控件
视图状态的保存
重构页面/控件
初始化页面/控件/IsPostBack
 
了解一下控件的生命周期。
1:Instantiate
2:Initialize
3:Begin Tracking View State
4:Load View State (postback only)
5:Load Postback Data (postback only)
6:Load
7:Raise Changed Events (postback only, optional)
8:Raise Postback Events (postback only, optional)
9:PreRender
10:SaveViewState
11:Render
12:Unload
13:Dispose
视图状态:ViewState,asp.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)里面)。
这部分,需要了解不少关于ViewState和asp.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。比如这样:

Page_Load都在初始化这个控件)。如果我们把应用放到UserControl上,我们动态加载的是一个UserControl,而这个Control的初始化事件又非常耗时

 

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

 


这样呢?看上去可以了。但是问题是,你做了重复的初始化工作(每次
比如:

 

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来完成的。
3:Rebuild的时候一定不要调用子控件的初始化的函数,这样会浪费时间。
 
问题:可不可以通过设置UserControl的IsPostBack属性来达到一种和Page类似的处理方式呢?这样在UserControl里面就可以使用if(!IsPostBack)来做一些数据初始化了。
我记得我曾经看过一篇文章,可以在某个事件中设置IsPostBack属性,但是现在怎么也找不到这篇文章了,可惜得很。
 
二:
用DataBinder.eval 绑定不必关心数据来源(Dataread或dataset)。不必关心数据的类型eval会把这个数据对象转换为一个字符串。在底层绑定做了很多工作,使用了反射性能。正因为使用方便了,但却影响了数据性能。当于dataset绑定时,DataItem其实式一个DataRowView(如果绑定的是一个数据读取器(dataread)它就是一个IdataRecord。)因此直接转换成DataRowView的话,将会给性能带来很大提升。
     使用时注意:1.注意字段名的大小写(要特别注意)。如果和查询的不一致,在某些情况下会导致比<%# DataBinder.Eval(Container.DataItem, "字段名") %>还要慢。2.如果想进一步提高速度,可采用<%# ((System.Data.DataRowView)Container.DataItem)[0] %>的方法。不过其可读性不高。

// 以DataSet做数据源时

// 正常情况(C#)

<% # ((System.Data.DataRowView)Container.DataItem)[ " Title " %>


// 绑定日期字段格式字符串

<% # Convert.ToDateTime(((System.Data.DataRowView)Container.DataItem)

[
" FbTime " ]).ToString( " yyyy年MM月dd日 " %>

<% # DataBinder.Eval(Container,  " DataItem.FbTime " , " {0:yyyy年MM月dd日} " %>


// 字符串绑定超过指定长度截断

<% # DataBinder.Eval(Container.DataItem,  " Text " ).ToString().Trim().Length > 7 ?

DataBinder.Eval(Container.DataItem, 
" Text " ).ToString().Trim().Substring

(
0 , 7 ):DataBinder.Eval(Container.DataItem,  " Text " ).ToString().Trim()  %>


// 价格格试化为2位小数

<% # Convert.ToDecimal(((System.Data.DataRowView)Container.DataItem) [ " Price " ]).ToString( " F2 " %>

<% # DataBinder.Eval(Container,  " DataItem.Price " , " {0:F2} " %>

' VB.net中价格格式化后判断
< %#  IIf (DataBinder.Eval(Container.DataItem,  " price " , " {0:F2} " ) = " 0.00 " , " " ,DataBinder.Eval(Container.DataItem,  " price " , " { 0 :F2} " )+ " " ) %>

' Vb.net常规
< %#  Ctype (Container.DataItem,DataRowView).Row( " Title " ) % >  

       

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值