.Net 2.0 中的动态控件解决方案

     大家知道,.Net2.0中的动态控件一直被微软视为后娘生的,怎么说呢,就是动态创建的控件不维护ViewState信息,PostBack一下,动态控件全消失了,
最常用的解决办法就是把动态控件放在OnInit事件中创建,并且放在IsPostback之外,可是这样一来就非常得不方便了,如果一定要在按钮的Click事件
中处理怎么办呢?下面说说我的解决办法,也许很菜,请不要见笑。

  这要从Postback机制说起,当页面正常载入以后,页面会递归的执行控件的 TrackViewState方法,也就是跟踪控件的ViewState状态是否更改,等页面完后生成以后,
页面又会递归的调用控件的SaveViewState方法以保存控件的ViewState状态,然后当页面PostBack时,页面又会递归地执行控件的LoadViewState方法以还原控件的
ViewState状态,所以,要保存动态控件的ViewState状态也必须分三步。

完整的代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Reflection;

namespace Sxw888.Base
{
    public class DynamicControlPage : System.Web.UI.Page
    {
        private List<DynamicControlItem> dynamicControlItems = new List<DynamicControlItem>();

        protected void TrackControlViewState(System.Web.UI.Control control)
        {
            Type type = typeof(System.Web.UI.Control);
            type.InvokeMember("TrackViewState", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, control, null);
        }

        protected void AddDynamicControlPageToTree(System.Web.UI.Control control)
        {
            if (!this.EnableViewState)
            {
                throw new System.Exception("当Page的EnableViewState为false时不能添加动态控件!");
            }

            if (control.Parent == null) throw new System.Exception("请将该控件添加入控件树!");

            this.AddDynamicControlPageToTreePrivate(control);
        }

        private void AddDynamicControlPageToTreePrivate(System.Web.UI.Control control)
        {
            if (!control.EnableViewState) return;

            if (!String.IsNullOrEmpty(control.ID))
            {
                if (this.FindDynamicControl(control.ID) != null) return;
            }
            if (String.IsNullOrEmpty(control.ID)) control.ID = control.UniqueID;//new Guid().ToString();
            DynamicControlItem item = new DynamicControlItem();
            item.Control = control;
            item.Parameter = new DynamicControlParameter();
            item.Parameter.Id = control.ID;
            item.Parameter.ParentId = control.Parent.ID;
            item.Parameter.TypeName = control.GetType().AssemblyQualifiedName;
            item.Parameter.ViewState = null;
            this.dynamicControlItems.Add(item);
            if (control.HasControls())
            {
                foreach (System.Web.UI.Control c in control.Controls)
                {
                    this.AddDynamicControlPageToTreePrivate(c);
                }
            }
        }

        public System.Web.UI.Control FindDynamicControl(string Id)
        {
            foreach (DynamicControlItem item in this.dynamicControlItems)
            {
                if (item.Control.ID == Id) return item.Control;
            }
            return null;
        }


        protected virtual override void LoadViewState(object savedState)
        {
            Pair p = (Pair)savedState;
            DynamicControlParameter[] DynamicControlParameters = (DynamicControlParameter[])p.Second;
            foreach (DynamicControlParameter Paremeter in DynamicControlParameters)
            {
                Type type = Type.GetType(Paremeter.TypeName);
                ConstructorInfo construct = type.GetConstructor(new Type[] { });
                System.Web.UI.Control c = (System.Web.UI.Control)construct.Invoke(new object[] { });
                type.InvokeMember("LoadViewState", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, c, new object[] { Paremeter.ViewState });
                c.ID = Paremeter.Id;

                if (this.FindControl(c.ID) != null) continue;
               
                System.Web.UI.Control parent = this.FindControl(Paremeter.ParentId);
                if (parent == null) parent = this.FindDynamicControl(Paremeter.ParentId);
                parent.Controls.Add(c);

                DynamicControlItem item = new DynamicControlItem();
                item.Parameter = Paremeter;
                item.Control = c;
                this.dynamicControlItems.Add(item);
            }
            base.LoadViewState(p.First);
        }

        protected virtual override object SaveViewState()
        {
            Pair p = new Pair();
            p.First = base.SaveViewState();
            List<DynamicControlParameter> objects = new List<DynamicControlParameter>();
            foreach (DynamicControlItem item in this.dynamicControlItems)
            {
                Type t = typeof(System.Web.UI.Control);

                object o = t.InvokeMember("SaveViewState", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, item.Control, null);
                item.Parameter.ViewState = o;
                objects.Add(item.Parameter);
            }
            p.Second = objects.ToArray();
            return p;
        }
    }

    [Serializable]
    internal class DynamicControlParameter
    {
        private string _TypeName;
        private string _Id;
        private string _ParentId;
        private object _ViewState;

        public string TypeName
        {
            get { return this._TypeName; }
            set { this._TypeName = value; }
        }

        public string Id
        {
            get { return this._Id; }
            set { this._Id = value; }
        }

        public string ParentId
        {
            get { return this._ParentId; }
            set { this._ParentId = value; }
        }

        public object ViewState
        {
            get { return this._ViewState; }
            set { this._ViewState = value; }
        }
    }

    internal class DynamicControlItem
    {
        private System.Web.UI.Control _Control;
        private DynamicControlParameter _Paremeter;

        public System.Web.UI.Control Control
        {
            get { return this._Control; }
            set { this._Control = value; }
        }

        public DynamicControlParameter Parameter
        {
            get { return this._Paremeter; }
            set { this._Paremeter = value; }
        }
    }
}

上述代码的用法如下:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml.Serialization;

public partial class test : Sxw888.Base.DynamicControlPage
{

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            TextBox tb = new TextBox();
            this.TrackControlViewState(tb);
            tb.Text = "测试";
            this.Panel1.Controls.Add(tb)   //假设页面中有一Panel控件,并且ID为Panel1
            this.AddDynamicControlPageToTree(tb);
         }
    }

好,现在可以去测试一下,你会发现PostBack一下之后,动态生成的控件仍然还在,对了,如果还需要激活动态控件的事件的话,只需要重载一下LoadViewState方法即可,这里不作说明。


匆匆忙忙写一点,希望大家批评指正。
 

你可能感兴趣的:(.net)