海纳百川 有容乃大(http://www.brtech.com.cn)

海纳百川,有容乃大(http://www.brtech.com.cn)

  :: 首页 :: 博问 :: 闪存 :: :: 联系 :: 订阅 订阅 :: 管理 ::

有网友问及在ASP.NET中动态生成控件在回调时控件不复存在的问题(见帖子:☆★请教关于动态生成控件的问题!急等回复!!救命啊!!),针对这个问题,我提出的解决方案如下(由于工作原因,没有很好整理,改天稍有空闲再把这些内容整理一下形成一个完整解决方案的文档,相信这两个文档对大多数用户已经足够起到启发式的作用了*_*):

对于你的这个问题,我的解决方案是(没有办法实地测试,只能是根据你的描述和需求分析和拍脑袋解决了):

把你动态生成控件的代码写到一个过程中:

private void CreateDynamicControls()//具体需要传递什么的参数及返回什么样的值自己考虑了。
{
}


在这个过程中进行一个简单的处理:

1——
清除容器(父)控件中所有通过本方法动态生成的子控件
比如:PH1.Controls.Clear();

2——
生成需要的所有控件

3——
把这些控件通过Controls.Add添加到指定的容器(父)控件中(比如PH1.Controls.Add(ov))


这样你就模块化的实现了所需要控件的动态生成的过程,接下来就是你根据需要动态调用这个函数的问题了。


首先,这个方法应该在OnInit函数中调用,这样可以确保动态生成的控件在PostBack的时候仍然在页面中存在(系统还没有智能到这个地步,是不会自动生成的),否则,系统将在POSTBACK的时候不能获得动态生成的控件,导致数据丢失。

其次,在你的代码处理过程(事件处理过程)中,根据需要适当的调用这个函数,实现具体内容的动态修改。


这里要注意的几个问题:

1——
既然控件是动态生成的,那么第一次启动页面(IsPostBack为false时)页面中实际上是不存在动态生成的控件的,这个时候,要对在OnInit中对CreateDynamicControls的调用进行判断:

if(Request.Form["__VIEWSTATE"]!=null)
{
  //这里根据需要初始化并调用方法CreateDynamicControls
}

2——
在OnInit中调用函数CreateDynamicControls动态创建控件时,所创建的控件数量的控制以及其数据来源的问题。
这个问题的解决,可以采用ViewState来保存相关的数据信息:

假设,你动态生成控件时,是用户切换了分页、用户改变了搜索条件导致的,那么需要把当前的页码信息和搜索条件等信息保存到VIEWSTATE中,这样可以保证在OnInit中调用CreateDynamicControls函数时使用了相同的数据信息,从而保证PAGE在POSTBACK时动态生成控件的正确恢复。
在这个解决中,VIEWSTATE中的数据是晚于OnInit生成的,也就是说调用OnInit时,VIEWSTATE中还没有把数据有效的生成,因此保存到VIEWSTATE中是不可取的,这里可以采取迂回的解决方案:以前写ASP时的方法:

A——
定义一个HIDDEN类型的INPUT,设置RUNAT=SERVER,ID为HIDDENTEXT(假设这个ID)

B——
在事件PreRender中,实现上面数据信息(如页码、搜索条件等)进行处理后保存到HIDDEN域中:
HIDDENTEXT.Value=...

C——
在OnInit中:
if(Request.Form["__VIEWSTATE"]!=null)
{
  string strHiddenText=Request.Form[HIDDENTEXT.UniqueID];
  //这里分析strHiddenText,把相关信息回复到对应的变量中
  //这里根据需要初始化并调用方法CreateDynamicControls
}


在这个解决数据信息的方案中,存在一个问题,就是数据库的信息可能随时都在变,虽然有页码信息和搜索条件信息,但当PAGE POSTBACK的时候,按照这些条件获取的数据并不是原来的数据(因为数据在变呀!),怎么办?

解决:

a——
动态生成控件时用到的数据序列化后保存到上面的HIDDENTEXT中,在OnInit中在获取了HIDDENTEXT的数据后再进行反序列化获得数据。
这个方法有个麻烦:数据量大时,页面往返传输大量的数据浪费大量的带宽资源

b——
针对a中的解决方案带宽资源浪费大的问题,这里采取新的解决方案:使用CACHE来存取动态控件用到的数据
Cache["DynamicControlsDataSet"]=ds;//动态数据的数据集DataSet,具体自己确定
这样在OnInit中:
if(Request.Form["__VIEWSTATE"]!=null)
{
  DataSet ds=(DataSet)Cache["DynamicControlsDataSet"];
  //这里分析strHiddenText,把相关信息回复到对应的变量中
  //这里根据需要初始化并调用方法CreateDynamicControls
}
这里还存在的一个问题是:Cache是共享使用的,会导致数据的干扰!解决:使用GUID来处理:

::定义变量
private string m_strGuid;

::保存数据
在PreRender事件处理中:
if(m_strGuid==null)
{
  m_strGuid=System.Guid.NewGuid().ToString();
}
Cache[m_strGuid]=ds;
HIDDENTEXT.Value=m_strGuid;

在OnInit中:
if(Request.Form["__VIEWSTATE"]!=null)
{
  m_strGuid=Request.Form[HIDDENTEXT.UniqueID];
  DataSet ds=(DataSet)Cache[m_strGuid];
  //这里分析strHiddenText,把相关信息回复到对应的变量中
  //这里根据需要初始化并调用方法CreateDynamicControls
}

到这里,本问题的解决就大功告成了,下面就是你在具体应用中代码的实现问题了。
鉴于这个问题,我一直忙于工作(即便有点时间也WOW了^_^),所以一直未能提供示例代码,下面进行简要的补充描述。

1——
首先,假设我们的应用中用到4个步骤,每个步骤的具体界面和操作不同,但框架兼容,我们定义这4个步骤的不同部分到4个UserControl中(就是ascx),分别为UC0、UC1、UC2、UC3(.ascx)

2——
根据应用需求,我们定义好用户操作的执行页面(aspx),这个页面我们简单点:一个runat=server的form中放置一个placeholder控件(id为container)和导航按钮(上一步、下一步,均runat=server)以及一个hidden类型的input对象(runat=server, Name/ID为stepText)

3——
为这个页面编写代码:
定义以下变量:
UserControl uc;//当前页面显示的用户控件(即步骤1定义的4个UC之一)
int step=0;//当前步骤索引,默认为0,即第一步

protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if(Request.Form.Length != 0)
{
//post提交状态,如果非提交状态则长度为0(不能用IsPostback判断)
step = int.Parse(Request.Form["stepText"];
}
//动态加载步骤对应的uc
uc = LoadControl(string.Format("UC{0}.ascx", step));
container.Controls.Add(uc);
}

private void PreviousStep()
{
//上一步处理
step--;
}

private void NextStep()
{
//下一步处理
step++;
}

protected override void OnPreRender(EventArgs e)
{
//清除上一次的界面控件
container.Controls.Clear();
//生成当前步骤的界面
uc = LoadControl(string.Format("UC{0}.ascx", step));
container.Controls.Add(uc);
//保存当前步骤状态
stepText.value = step.ToString();
}

posted on 2006-03-26 04:25  阿昆  阅读(657)  评论(0编辑  收藏  举报