ASP.NET maintain control state that has collection items

╄→гoц情女王★ 提交于 2019-12-13 02:11:22

问题


Interesting. I have a control QuickContacts with Contacts collection. When I declare Contacts of type List<Contact> it works perfectly, but when use ContactsList<Contact> which is a Generic that implements IList<T> it gives "Parser Error : Type 'Controls.ContactList'1[[Controls.Contact, Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' does not have a public property named 'Contact'." Here, this code works perfectly with List<Contact>

[DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
    private List<Contact> contactsList;

    [Category("Behavior"),
    Description("The contacts collection."),
    DesignerSerializationVisibility(
        DesignerSerializationVisibility.Content),
    PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public List<Contact> Contacts
    {
        get
        {
            if (contactsList == null)
            {
                contactsList = new List<Contact>();
            }
            return contactsList;
        }
    }

I want to provide a Contacts collection that maintains state so I replace using List<Contacts> with custom ContactsList that implements IList<T>, IStateManager. When use IList<T> instead of List<T>, it doesn't work. Here is the

public class ContactList<T> : IList<T>, IStateManager
{

Then I use it as following:

[DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
    private ContactList<Contact> contactsList;

    [Category("Behavior"),
    Description("The contacts collection."),
    DesignerSerializationVisibility(
        DesignerSerializationVisibility.Content),
    PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public ContactList<Contact> Contacts
    {
        get
        {
            if (contactsList == null)
            {
                contactsList = new ContactList<Contact>();
            }
            return contactsList;
        }
    }

"Parser Error : Type 'Controls.ContactList'1[[Controls.Contact, Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' does not have a public property named 'Contact'."
Parser Error at <cc1:Contact Name=

<asp:Content ID="Content2" ContentPlaceHolderID="Main" runat="server">
    <cc1:QuickContacts ID="QuickContacts1" runat="server" BorderStyle="Solid" BorderWidth="1px">
        <cc1:Contact Name="someone" Email="someone@example.com"
        Phone="(555) 555-0100" />

I read many posts about List<T> vs IList<T> but that still doesn't answer the question. What difference between List<T> and a class that implements IList<T> which causes this error?


回答1:


Here is the answer, when reviewed List<T> implementation at Microsoft http://referencesource.microsoft.com

[DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
{

It implements System.Collections.IList too. And this was the point.

And here is the full working code.

[DefaultProperty("Contacts"),
ParseChildren(true, ChildrenAsProperties = true, DefaultProperty = "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
    private StateManagedCollection<Contact> contactsList;

    [Category("Behavior"),
    Description("The contacts collection."),
    DesignerSerializationVisibility(
        DesignerSerializationVisibility.Content),
    PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public StateManagedCollection<Contact> Contacts
    {
        get
        {
            if (contactsList == null)
            {
                contactsList = new StateManagedCollection<Contact>();
            }
            return contactsList;
        }
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {

        Table t = CreateContactsTable();
        if (t != null)
        {
            t.RenderControl(writer);
        }
    }

    private Table CreateContactsTable()
    {
        Table t = null;

        if (contactsList != null && contactsList.Count > 0)
        {
            t = new Table();

            foreach (Contact item in contactsList)
            {
                Contact aContact = item as Contact;

                if (aContact != null)
                {
                    TableRow row = new TableRow();

                    TableCell c1 = new TableCell();
                    c1.Text = aContact.Name;
                    row.Controls.Add(c1);

                    TableCell c2 = new TableCell();
                    c2.Text = aContact.Email;
                    row.Controls.Add(c2);

                    TableCell c3 = new TableCell();
                    c3.Text = aContact.Phone;
                    row.Controls.Add(c3);

                    t.Controls.Add(row);
                }
            }
        }

        return t;
    }

    protected override void LoadViewState(object savedState)
    {
        if (savedState != null)
        {
            Pair p = savedState as Pair;
            base.LoadViewState(p.First);
            contactsList.LoadViewState(p.Second);
        }
    }

    protected override object SaveViewState()
    {
        Pair p = new Pair(base.SaveViewState(), contactsList.SaveViewState());
        return p;
    }

    protected override void TrackViewState()
    {
        base.TrackViewState();
        contactsList.TrackViewState();
    }
}

And here is the StateManagedCollection

    public class StateManagedCollection<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>, IStateManager
        where T : StateManagedClass, new()
{
    private List<T> lst = default(List<T>);
    private Boolean isTrackingViewState = default(Boolean);
    private Boolean saveAll = default(Boolean);

    public StateManagedCollection()
    {
        lst = new List<T>();
        isTrackingViewState = false;
        saveAll = false;
    }

    public void SetMarked()
    {
        for (int i = 0; i < lst.Count; i++)
        {
            lst[i].SetMarked();
        }
    }

    public bool IsTrackingViewState
    {
        get { return isTrackingViewState; }
    }

    public void LoadViewState(object state)
    {
        if (state != null)
        {

            if (state is List<Object>)
            {
                // all items were saved

                List<Object> allItems = (List<Object>)state;
                saveAll = true;
                Int32 count = lst.Count;
                for (int i = 0; i < allItems.Count; i++)
                {
                    if (i < count)
                        lst[i].LoadViewState(allItems[i]);
                    else
                    {
                        T item = new T();
                        item.LoadViewState(allItems[i]);
                        lst.Add(item);
                    }
                }
            }
            else if (state is Dictionary<Int32, Object>)
            {
                Dictionary<Int32, Object> changedItems = (Dictionary<Int32, Object>)state;
                foreach (KeyValuePair<Int32, Object> item in changedItems)
                {
                    if (item.Key < lst.Count)
                        lst[item.Key].LoadViewState(item.Value);
                }
            }
        }
    }

    public object SaveViewState()
    {
        if (saveAll)
        {
            List<Object> allItems = new List<Object>();
            foreach (var item in lst)
            {
                item.SetMarked();
                allItems.Add(item.SaveViewState());
            }
            return allItems;
        }
        else
        {
            Dictionary<Int32, Object> changedItems = new Dictionary<Int32, Object>();
            for (int i = 0; i < lst.Count; i++)
            {
                Object state = lst[i].SaveViewState();
                if (state != null)
                {
                    changedItems.Add(i, state);
                }
            }
            return changedItems;
        }
    }

    public void TrackViewState()
    {
        isTrackingViewState = true;
        for (int i = 0; i < lst.Count; i++)
        {
            lst[i].TrackViewState();
        }
    }

    public int IndexOf(T item)
    {
        return lst.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        lst.Insert(index, item);
        if (isTrackingViewState)
            saveAll = true;
    }

    public void RemoveAt(int index)
    {
        lst.RemoveAt(index);
        if (isTrackingViewState)
            saveAll = true;
    }

    public T this[int index]
    {
        get
        {
            return lst[index];
        }
        set
        {
            lst[index] = value;
        }
    }

    public void Add(T item)
    {
        lst.Add(item);
        if (isTrackingViewState)
            saveAll = true;
    }

    public void Clear()
    {
        lst.Clear();
        if (isTrackingViewState)
            saveAll = true;
    }

    public bool Contains(T item)
    {
        return lst.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        lst.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return lst.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        Boolean rslt = lst.Remove(item);
        if (isTrackingViewState)
            saveAll = true;
        return rslt;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return lst.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public int Add(object value)
    {
        Add((T)value);
        return Count - 1;
    }

    public bool Contains(object value)
    {
        return Contains((T)value);
    }

    public int IndexOf(object value)
    {
        return IndexOf((T)value);
    }

    public void Insert(int index, object value)
    {
        Insert(index, (T)value);
    }

    public bool IsFixedSize
    {
        get { return ((IList)lst).IsFixedSize; }
    }

    public void Remove(object value)
    {
        Remove((T)value);
    }

    object IList.this[int index]
    {
        get
        {
            return lst[index];
        }
        set
        {
            lst[index] = (T)value;
        }
    }

    public void CopyTo(Array array, int index)
    {
        CopyTo((T[])array, index);
    }

    public bool IsSynchronized
    {
        get { return ((ICollection)lst).IsSynchronized; }
    }

    public object SyncRoot
    {
        get { return ((ICollection)lst).SyncRoot; }
    }
}

And the StateManagedClass

public abstract class StateManagedClass : IStateManager
{
    private Boolean isTrackingViewState = default(Boolean);
    private StateBag viewState = default(StateBag);

    public StateManagedClass()
    {
        isTrackingViewState = false;
        viewState = new StateBag(false);
    }

    public virtual StateBag ViewState
    {
        get
        {
            return viewState;
        }
    }

    public bool IsTrackingViewState
    {
        get { return isTrackingViewState; }
    }

    public void LoadViewState(object state)
    {
        if (state != default(object))
        {
            ((IStateManager)viewState).LoadViewState(state);
        }
    }

    public object SaveViewState()
    {
        Object savedState = default(Object);

        if (viewState != null)
        {
            savedState =
                ((IStateManager)viewState).SaveViewState();
        }

        return savedState;
    }

    public void TrackViewState()
    {
        isTrackingViewState = true;

        if (viewState != default(StateBag))
        {
            ((IStateManager)viewState).TrackViewState();
        }
    }

    public void SetMarked()
    {
        viewState.SetDirty(true);
    }
}

And the Contact class

public class Contact : StateManagedClass 
{
    [
    Category("Behavior"),
    DefaultValue(""),
    Description("Name of contact"),
    NotifyParentProperty(true)
    ]
    public String Name
    {
        get
        {
            Object s = ViewState["Name"];
            return (s == null) ? String.Empty : (String)s;
        }
        set
        {
            ViewState["Name"] = value;
        }
    }

    [
    Category("Behavior"),
    DefaultValue(""),
    Description("Email address of contact"),
    NotifyParentProperty(true)
    ]
    public String Email
    {
        get
        {
            Object s = ViewState["Email"];
            return (s == null) ? String.Empty : (String)s;
        }
        set
        {
            ViewState["Email"] = value;
        }
    }

    [
    Category("Behavior"),
    DefaultValue(""),
    Description("Phone number of contact"),
    NotifyParentProperty(true)
    ]
    public String Phone
    {
        get
        {
            Object s = ViewState["Phone"];
            return (s == null) ? String.Empty : (String)s;
        }
        set
        {
            ViewState["Phone"] = value;
        }
    }

}

Test the control:

<%@ Page Title="" Language="C#" MasterPageFile="~/RTL.Master" AutoEventWireup="true" CodeBehind="WebForm15.aspx.cs" Inherits="Controls.WebForm15" %>

<%@ Register Assembly="Controls" Namespace="Controls" TagPrefix="cc1" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Main" runat="server">
    <cc1:QuickContacts ID="QuickContacts1" runat="server" BorderStyle="Solid" BorderWidth="1px">
        <cc1:Contact Name="someone" Email="someone@example.com"
            Phone="(555) 555-0100" />
        <cc1:Contact Name="jae" Email="jae@fourthcoffee.com"
            Phone="(555) 555-0101" />
        <cc1:Contact Name="lene" Email="lene@contoso.com"
            Phone="(555) 555-0102" />
    </cc1:QuickContacts>
    <br />
    <asp:Button runat="server" ID="Button1" Text="Add" OnClick="Button1_Click"></asp:Button>
    <asp:Button runat="server" Text="Refresh"></asp:Button>
    <br />
    <br />
    <asp:HyperLink ID="HyperLink1" NavigateUrl="~/WebForm15.aspx"
        runat="server">
    Reload Page</asp:HyperLink>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
</asp:Content>

And the codebehind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Controls
{
    public partial class WebForm15 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            SkolaControlsWorkings.Controls.Contact contact = new SkolaControlsWorkings.Controls.Contact();
            contact.Name = "Name";
            contact.Email = "Email@mai.com";
            contact.Phone = "(111) 111-1111";
            QuickContacts1.Contacts.Add(contact);
            Button1.Visible = false;
        }
    }
}

Now you can see how pressing the "Add" button causes new contact item to be added to the list, and how the new added contacts are maintained through page postbacks.
This is should be a complete working example, have fun.
Here is the MSDN original example Web Control Collection Property Example



来源:https://stackoverflow.com/questions/31947655/asp-net-maintain-control-state-that-has-collection-items

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!