Having trouble with ViewState and programatically added / removed columns

8 posts, 2 answers
  1. Lars-Erik
    Lars-Erik avatar
    9 posts
    Member since:
    Nov 2010

    Posted 17 Nov 2010 Link to this post

    Hi!

    I've been fiddling a bit with the new TreeList control, and manage to add some template columns dynamically.
    It seems it's ok to remove a few and add more, but when I remove a lot and add less, I get an exception saying


    Invalid column type: "TreeListTemplateColumn".

    [Exception: Invalid column type: "TreeListTemplateColumn".]
       Telerik.Web.UI.RadTreeList.CreateColumnByType(String columnType) +184
       Telerik.Web.UI.TreeListColumnsCollection.System.Web.UI.IStateManager.LoadViewState(Object state) +350
       Telerik.Web.UI.RadTreeList.LoadViewState(Object savedState) +97
       System.Web.UI.Control.LoadViewStateRecursive(Object savedState) +183
       System.Web.UI.Control.LoadChildViewStateByIndex(ArrayList childState) +134
       System.Web.UI.Control.LoadViewStateRecursive(Object savedState) +221
       System.Web.UI.Control.LoadChildViewStateByIndex(ArrayList childState) +134
       System.Web.UI.Control.LoadViewStateRecursive(Object savedState) +221
       System.Web.UI.Page.LoadAllState() +312
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1661

    This also happens when "a lot" of columns has been added and I try to collapse a section.

    I guess it's pretty easy to reproduce if you just fiddle a bit with the following page / code.
    Try for instance to expand "A", then click "Month", and try to collapse "A".
    Or just click "Month" then "Quarter".

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TreeViewTimeline.aspx.cs"
        Inherits="Web20.TreeViewTimelineTelerik.TreeViewTimeline" EnableEventValidation="false" %>
      
    <%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <div>
            <asp:LinkButton ID="QtrButton" runat="server" Text="Quarter" OnClick="QtrButtonClicked" />
            <asp:LinkButton ID="MonthButton" runat="server" Text="Month" OnClick="MonthButtonClicked" />
          
            <div id="tbl-container">
                <telerik:RadTreeList runat="server" ID="treeList" Skin="Vista" AutoGenerateColumns="False"
                    OnInit="treeListInit"
                    DataKeyNames="ID" ParentDataKeyNames="Parent" OnItemCommand="treeListItemCommand"
                      
                    >
                    <Columns>
                        <telerik:TreeListTemplateColumn UniqueName="Name" HeaderText="Navn" >
                            <HeaderStyle />
                            <ItemStyle />
                            <ItemTemplate>
                                <asp:TextBox runat="server" ID="NameTextBox" Text='<%# Eval("Name") %>' />
                            </ItemTemplate>
                        </telerik:TreeListTemplateColumn>
                    </Columns>
                </telerik:RadTreeList>
            </div>
        </div>
        </form>
    </body>
    </html>

    using System;
    using System.Collections.Generic;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Web.UI;
      
    namespace Web20.TreeViewTimelineTelerik
    {
        public partial class TreeViewTimeline : System.Web.UI.Page
        {
            public string Mode
            {
                get { return ViewState["mode"] as string ?? "Quarter"; }
                set { ViewState["mode"] = value; }
            }
      
            protected void Page_Load(object sender, EventArgs e)
            {
                if (!IsPostBack)
                {
                    BindList();
                }
            }
      
            private void BindList() 
            {
                var list = new[]
                           {
                            new Item {ID = "A", Name = "A", Parent = "", Periods = new Dictionary<string, Period>
                                  {{"Q1", new Period {Name = "Q1"}}, {"Feb", new Period {Name="1/10"}}}}
                            , new Item {ID = "B", Name = "B", Parent = ""}
                            , new Item {ID = "A.A", Name = "A.A", Parent = "A", Periods = new Dictionary<string, Period> 
                                  {{"Q2", new Period { Name = "Qtr 2"}}, {"Q3", new Period {Name="Qty 3"}}, {"Mar", new Period{Name="2/10"}} }}
                           };
      
                treeList.DataSource = list;
                treeList.DataBind();
            }
      
            protected void treeListItemCommand(object sender, TreeListCommandEventArgs e)
            {
                if (e.CommandName == RadTreeList.ExpandCollapseCommandName)
                {
                    ResetColumns();
                }
            }
      
            protected void treeListInit(object sender, EventArgs e)
            {
                InitColumns();
            }
      
            private void InitColumns()
            {
                switch(Mode)
                {
                    case "Quarter":
                        string[] quarters = new[] {"Q1", "Q2", "Q3", "Q4"};
                        foreach(string quarter in quarters)
                            treeList.Columns.Add(new TreeListTemplateColumn{UniqueName = quarter, HeaderText = quarter, ItemTemplate = new PeriodTemplate(quarter)});
                        break;
                    case "Month":
                        string[] months = new[] {"Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"};
                        foreach(string month in months)
                            treeList.Columns.Add(new TreeListTemplateColumn { UniqueName = month, HeaderText = month, ItemTemplate = new PeriodTemplate(month) });
                        break;
                }
            }
      
            private void ResetColumns() {
                RemoveDynamicColumns();
                InitColumns();
                BindList();
            }
      
            private void RemoveDynamicColumns()
            {
                for(int i = treeList.Columns.Count - 1; i>=1; i--)
                    treeList.Columns.RemoveAt(i);
            }
      
            protected void QtrButtonClicked(object sender, EventArgs e)
            {
                Mode = "Quarter";
                ResetColumns();
            }
      
            protected void MonthButtonClicked(object sender, EventArgs e)
            {
                Mode = "Month";
                ResetColumns();
            }
        }
      
        public class PeriodTemplate : ITemplate
        {
            private readonly string quarter;
      
            public PeriodTemplate(string quarter)
            {
                this.quarter = quarter;
            }
      
            public void InstantiateIn(Control container)
            {
                Label label = new Label();
                label.EnableViewState = false;
                label.ID = quarter + "Label";
                Item dataItem = (Item)DataBinder.GetDataItem(container.BindingContainer);
                if (dataItem != null && dataItem.Periods != null)
                {
                    if (dataItem.Periods.ContainsKey(quarter))
                        label.Text = dataItem.Periods[quarter].Name;
                }
                container.Controls.Add(label);
            }
        }
      
        public class Item
        {
            public string ID { get; set; }
            public string Parent { get; set; }
            public string Name { get; set; }
      
            public Dictionary<string, Period> Periods { get; set; }
        }
      
        public class Period
        {
            public string Name { get; set; }
        }
    }

    Hope you guys can help. :)

    Lars-Erik

  2. Veli
    Admin
    Veli avatar
    2002 posts

    Posted 23 Nov 2010 Link to this post

    Hello Lars-Erik,

    You need to create RadTreeList entirely in the Page_Init event and add it to a container on every postback. This is the only supported approach of initialization if you need to dynamically change the treelist columns structure.

    Regards,
    Veli
    the Telerik team
    Browse the vast support resources we have to jumpstart your development with RadControls for ASP.NET AJAX. See how to integrate our AJAX controls seamlessly in SharePoint 2007/2010 visiting our common SharePoint portal.
  3. UI for ASP.NET Ajax is Ready for VS 2017
  4. Veli
    Admin
    Veli avatar
    2002 posts

    Posted 23 Nov 2010 Link to this post

    Just to clarify, any modifications to the RadTreeList structure need to happen in Page_Init. This means you cannot use a postback event to modify the columns collection. You need to change the columns when the control is recreated.

    Veli
    the Telerik team
    Browse the vast support resources we have to jumpstart your development with RadControls for ASP.NET AJAX. See how to integrate our AJAX controls seamlessly in SharePoint 2007/2010 visiting our common SharePoint portal.
  5. Lars-Erik
    Lars-Erik avatar
    9 posts
    Member since:
    Nov 2010

    Posted 02 Dec 2010 Link to this post

    Hi again, thanks for the input!

    It still has trouble. When adding columns in Page_Init (OnInit) it still throws "Invalid column type "TreeListTemplateColumn" (my class) from LoadViewState on PostBack. Even when not modifying the columns.
    I'm starting to believe this i a problem with code templates, and not when they're added?

    Here's the modified code, I've removed the grid entirely from the aspx.

    public partial class TreeViewTimeline : System.Web.UI.Page
    {
        private RadTreeList treeList;
        public string Mode
        {
            get { return ViewState["mode"] as string ?? "Quarter"; }
            set { ViewState["mode"] = value; }
        }
        protected override void OnInit(EventArgs e)
        {
            treeList = new RadTreeList();
            treeList.ID = "treeList";
            treeList.ItemCommand += TreeListItemCommand;
            treeList.Skin = "Vista";
            treeList.AutoGenerateColumns = false;
            treeList.DataKeyNames = new[] {"ID"};
            treeList.ParentDataKeyNames = new[] {"Parent"};
            gridContainer.Controls.Add(treeList);
            InitColumns();
            base.OnInit(e);
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                BindList();
            }
        }
        private void BindList() 
        {
            var list = new[]
                       {
                        new Item {ID = "A", Name = "A", Parent = "", Periods = new Dictionary<string, Period>
                              {{"Q1", new Period {Name = "Q1"}}, {"Feb", new Period {Name="1/10"}}}}
                        , new Item {ID = "B", Name = "B", Parent = ""}
                        , new Item {ID = "A.A", Name = "A.A", Parent = "A", Periods = new Dictionary<string, Period> 
                              {{"Q2", new Period { Name = "Qtr 2"}}, {"Q3", new Period {Name="Qty 3"}}, {"Mar", new Period{Name="2/10"}} }}
                       };
            treeList.DataSource = list;
            treeList.DataBind();
        }
        protected void TreeListItemCommand(object sender, TreeListCommandEventArgs e)
        {
            if (e.CommandName == RadTreeList.ExpandCollapseCommandName)
            {
                BindList();
            }
        }
        private void InitColumns()
        {
            treeList.Columns.Add(new TreeListTemplateColumn{UniqueName = "Name", HeaderText = "Name", ItemTemplate = new TextBoxTemplate("Name")});
            switch(Mode)
            {
                case "Quarter":
                    string[] quarters = new[] {"Q1", "Q2", "Q3", "Q4"};
                    foreach(string quarter in quarters)
                        treeList.Columns.Add(new TreeListTemplateColumn{UniqueName = quarter, HeaderText = quarter, ItemTemplate = new PeriodTemplate(quarter)});
                    break;
                case "Month":
                    string[] months = new[] {"Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"};
                    foreach(string month in months)
                        treeList.Columns.Add(new TreeListTemplateColumn { UniqueName = month, HeaderText = month, ItemTemplate = new PeriodTemplate(month) });
                    break;
            }
        }
    }
    public class PeriodTemplate : ITemplate
    {
        private readonly string quarter;
        public PeriodTemplate(string quarter)
        {
            this.quarter = quarter;
        }
        public void InstantiateIn(Control container)
        {
            Label label = new Label();
            label.ID = quarter + "Label";
            Item dataItem = (Item)DataBinder.GetDataItem(container.BindingContainer);
            if (dataItem != null && dataItem.Periods != null)
            {
                if (dataItem.Periods.ContainsKey(quarter))
                    label.Text = dataItem.Periods[quarter].Name;
            }
            container.Controls.Add(label);
        }
    }
    public class TextBoxTemplate : ITemplate
    {
        private readonly string field;
        public TextBoxTemplate(string field)
        {
            this.field = field;
        }
        public void InstantiateIn(Control container)
        {
            TextBox textBox = new TextBox();
            textBox.EnableViewState = true;
            textBox.ID = field + "TextBox";
            Item dataItem = (Item)DataBinder.GetDataItem(container.BindingContainer);
            if (dataItem != null)
                textBox.Text = dataItem.Name;
            container.Controls.Add(textBox);
        }
    }
    public class Item
    {
        public string ID { get; set; }
        public string Parent { get; set; }
        public string Name { get; set; }
        public Dictionary<string, Period> Periods { get; set; }
    }
    public class Period
    {
        public string Name { get; set; }
    }
  6. Answer
    Veli
    Admin
    Veli avatar
    2002 posts

    Posted 02 Dec 2010 Link to this post

    Hi Lars-Erik,

    Try disabling the ViewState of the RadTreeList container. You will need to rebind RadTreeList on every postback in this scenario. Attaching a test page using a similar approach. Note how I have used RadTreeList's NeedDataSource event to pass the data source and only call Rebind() where I need to bind it.

    Veli
    the Telerik team
    Browse the vast support resources we have to jumpstart your development with RadControls for ASP.NET AJAX. See how to integrate our AJAX controls seamlessly in SharePoint 2007/2010 visiting our common SharePoint portal.
  7. Lars-Erik
    Lars-Erik avatar
    9 posts
    Member since:
    Nov 2010

    Posted 02 Dec 2010 Link to this post

    Thanks!
    Looks good, except I can't get it to collapse after expanding?

    L-E
  8. Answer
    Veli
    Admin
    Veli avatar
    2002 posts

    Posted 02 Dec 2010 Link to this post

    Confirmed. I have reported this issue for investigation. It seems, for now, there is no way RadTreeList columns can be changed dynamically without breaking the control's functionality. What you can do is add all your columns you would ever need in RadTreeList and selectively show/hide the ones you need/do not need.
    Veli
    the Telerik team
    Browse the vast support resources we have to jumpstart your development with RadControls for ASP.NET AJAX. See how to integrate our AJAX controls seamlessly in SharePoint 2007/2010 visiting our common SharePoint portal.
  9. Lars-Erik
    Lars-Erik avatar
    9 posts
    Member since:
    Nov 2010

    Posted 02 Dec 2010 Link to this post

    OK, thanks.
    Looking forward to a future release then. :)

    Lars-Erik
Back to Top
UI for ASP.NET Ajax is Ready for VS 2017