locked
Exception with custom control (complete minimal example included!) RRS feed

  • Question

  • User260076833 posted

    Dear community,

    I need help with a custom control. I already made experience with some custom controls, but this one has some specific difficulties. I swear, I did a really hard work to extract the problem from my real-life project and reduce it to a minimal example. I believe that it could not be much smaller. So give it a try, don't let me down! :-)

    Here we go:

    I want one control that has a TreeView to the left and a MultiView to the right. Each tree node in the tree has a type. For each type there is a corresponding "page", which should be displayed in the MultiView when a node with the corresponding type is selected.

    This control should be called a "TreePageView"; the tree nodes are called "TreePageNode"; the pages are called "TreePage".
    I want to declare this with the following markup:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Test.aspx.cs" Inherits="Playground.lab.TreePageView.Test" %>
    
    <%@ Register Assembly="Playground" Namespace="Playground.lab.TreePageView" TagPrefix="cmp" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
    
                <cmp:TreePageView ID="tpv" runat="server" Text="Text of the news">
    
                    <Nodes>
                        <cmp:TreePageNode Text="Things">
                            <cmp:TreePageNode Value="ID-1" Text="Bradley" type="emp" />
                            <cmp:TreePageNode Value="ID-2" Text="Whitney" type="emp" />
                            <cmp:TreePageNode Value="ID-3" Text="Barbara" type="emp" />
                            <cmp:TreePageNode Value="ID-4" Text="Company Car" type="car" />
                            <cmp:TreePageNode Value="ID-5" Text="Mail Car" type="car" />
                        </cmp:TreePageNode>
                    </Nodes>
    
                    <Pages>
                        <cmp:TreePage runat="server" ID="P1" type="emp" Caption="Employer">
                            <asp:Label ID="lbl" runat="server" Text="This is an Employer" />
                        </cmp:TreePage>
    
                        <cmp:TreePage runat="server" ID="P2" type="car" Caption="Car">
                            <asp:Label ID="Label1" runat="server" Text="This is a Car" />
                        </cmp:TreePage>
                    </Pages>
                </cmp:TreePageView>
    
            </div>
        </form>
    </body>
    </html>
    
    
    
    

    Here we have such a TreePageView, with a "Nodes" section and a "Pages" section.

    The Nodes section contains the tree nodes. There are some persons and some cars in the tree. The persons belong to the type "emp", which means "employer", while the cars belong to the type "car", which means "car".

    The Pages section contains one page for the type "emp" and one page for the type "car".
    So far so good.

    Here is the code behind for this page:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    namespace Playground.lab.TreePageView
    {
        public partial class Test : System.Web.UI.Page
        {
            private static bool crash = true; // if this is true, we crash when a node is selected
    
            protected void Page_Load(object snd, EventArgs evt)
            {
            }
    
            protected void Page_LoadComplete(object snd, EventArgs evt)
            {
                if (crash)
                {
                    TreePageNode n = new TreePageNode();
                    n.type = "car";
                    n.Text = "Evil node!";
                    tpv.Nodes.Add(n);
                }
            }
    
        }
    }

    Here you can see the member variable "crash". When this variable is set to true, the application exits with an exception described below whenever an arbitrary node in the tree is selected.

    The tree initially shows like this:

    When the variable crash is set to false, you can select any node and on the right you see "This is an Employer" or "This is a Car", respectively.

    When the variable crash is set to true, another node called "Evil node" is added to the tree in the Page_LoadComplete event. When you then select an arbitrary node, you get this exception:

    However, here is the code for TreePageNode.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI.WebControls;
    
    namespace Playground.lab.TreePageView
    {
        public class TreePageNode : TreeNode
        {
            public String type { get; set; }
        }
    }

    Here is the code for TreePage.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    namespace Playground.lab.TreePageView
    {
        [ParseChildren(false)]
        public class TreePage : Panel, INamingContainer
        {
            public String type { get; set; }
            public String Caption { get; set; }
        }
    }

    And here is the code for TreePageView.cs:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    namespace Playground.lab.TreePageView
    {
        public class TreePageView : CompositeControl 
        {
            private TreeView trv;
            private MultiView mtv;
            private ControlCollection col_Pages = null;
    
            public TreePageView() : base()
            {
            }
    
            [PersistenceMode(PersistenceMode.InnerDefaultProperty), DefaultValue(null), Browsable(false)]
            public virtual TreeNodeCollection Nodes
            {
                get
                {
                    if (trv == null)
                        trv = createTreeView();
    
                    return (trv.Nodes);
                }
            }
    
            [PersistenceMode(PersistenceMode.InnerDefaultProperty), DefaultValue(null), Browsable(false)]
            public virtual ControlCollection /*TreePageCollection*/ Pages
            {
                get
                {
                    if (col_Pages == null)
                        col_Pages = new ControlCollection (this);
    
                    return (col_Pages);
                }
            }
    
            protected override void CreateChildControls()
            {
                this.Controls.Clear();
                
                if (trv == null)
                    trv = createTreeView();
    
                mtv = createMultiView();
                Table tbl = createTable( trv,mtv);
                this.Controls.Add(tbl);
            }
    
            private TreeView createTreeView()
            {
                TreeView trv = new TreeView();
    
                trv.ShowLines = true;
                trv.ShowCheckBoxes = TreeNodeTypes.None;
                trv.SelectedNodeStyle.BackColor = Color.White;
                trv.SelectedNodeStyle.BorderWidth = Unit.Pixel(1);
                trv.SelectedNodeChanged += new EventHandler(tvw_SelectedNodeChanged);
    
                return (trv);
            }
    
            private MultiView createMultiView()
            {
                MultiView m = new MultiView();
    
                m.Views.Add(new View()); // empty view
    
                foreach (TreePage p in Pages)
                {
                    View v = new View();
    
                    v.Controls.Add(p);
                    m.Views.Add(v);
                }
    
                if (m.Views.Count > 0)
                    m.ActiveViewIndex = 0;
    
                return (m);
            }
    
            private Table createTable(Control ctl_l, Control ctl_r)
            {
                Table tbl = new Table();
                TableRow r = new TableRow();
     
                r = new TableRow();
                TableCell cl = new TableCell();
                TableCell cr = new TableCell();
    
                cl.Controls.Add(ctl_l);
                cr.Controls.Add(ctl_r);
                r.Cells.Add(cl);
                r.Cells.Add(cr);
                tbl.Rows.Add(r);
    
                return (tbl);
            }
    
            protected void tvw_SelectedNodeChanged(object snd, EventArgs evt)
            {
                TreeNode t = trv.SelectedNode;
                TreePageNode n = t as TreePageNode;
    
                if (n == null)
                    return;
    
                View v = getView(n.type);
    
                if (v == null)
                    v = mtv.Views[0]; // empty view
    
                mtv.SetActiveView(v);
            }
    
            private View getView (String type)
            {
                for (int i = 0; i < mtv.Views.Count; i++)
                {
                    View v = mtv.Views[i];
    
                    if (v.Controls.Count == 0)
                        continue;
    
                    Control c = v.Controls[0];
    
                    if (c == null)
                        continue;
    
                    if (!(c is TreePage))
                        continue;
    
                    TreePage p = (TreePage) c;
    
                    if (p.type != type)
                        continue;
    
                    return (v);
                }
    
                return (null);
            }
        }
    }

    Dear community, I believe that this minimal example shows my problem very clearly. But I cannot find out the reason for this myself.
    Please help!

    Thank you very much,
    Magnus

    Friday, December 9, 2016 7:10 PM

All replies

  • User753101303 posted

    Hi,

    Do you have the same problem if doing this in Page_Load (or even Page_Init?) rather than in Page_LoadComplete? For now it reminds me rather the usual issue with dynamic controls (ie you have to recreate them early enough so that the view state is consistent) rather than a problem with this particular control (and I believe you would just have the same problem with a standard treeview control).

    Monday, December 26, 2016 11:35 PM