RadTreeList in the DynamicData ManyToMany_Edit.ascx Control

3 posts, 0 answers
  1. Scott Morrison
    Scott Morrison avatar
    4 posts
    Member since:
    Oct 2007

    Posted 27 Jun 2011 Link to this post

    When using the RadTreeList in the DynamicData ManyToMany_Edit.ascx control, I am getting the following null ref error:

    [NullReferenceException: Object reference not set to an instance of an object.] Telerik.Web.UI.TreeListItemDecorator.PrepareDataItemsServiceCells(RadTreeList owner) +534 Telerik.Web.UI.RadTreeList.PrepareRows() +215 Telerik.Web.UI.RadTreeList.Render(HtmlTextWriter writer) +29 System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208 System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 System.Web.UI.Control.Render(HtmlTextWriter writer) +10 System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208 System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 System.Web.DynamicData.DynamicControl.Render(HtmlTextWriter writer) +154 ...

    Here is the code:

     

    <%@ Control Language="C#" CodeBehind="ManyToMany_Edit.ascx.cs" Inherits="Ctp.U.Web.ResidentPortal.Forms.ManyToMany_EditField" %>
    <%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
    <telerik:RadTreeList ID="_radTreeList1" runat="server" OnDataBound="_radTreeList1_DataBound">
    </telerik:RadTreeList>
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    using System.Web.DynamicData;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Web.UI;
     
    namespace Ctp.U.Web.ResidentPortal.Forms
    {
        public partial class ManyToMany_EditField : System.Web.DynamicData.FieldTemplateUserControl
        {
            public void Page_Load(object sender, EventArgs e)
            {
                // Register for the DataSource's updating event
                EntityDataSource ds = (EntityDataSource)this.FindDataSourceControl();
     
                // This field template is used both for Editing and Inserting
                ds.Updating += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting);
                ds.Inserting += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting);
            }
     
            void DataSource_UpdatingOrInserting(object sender, EntityDataSourceChangingEventArgs e)
            {
                MetaTable childTable = ChildrenColumn.ChildTable;
     
                // Comments assume employee/territory for illustration, but the code is generic
     
                // Get the collection of territories for this employee
                RelatedEnd entityCollection = (RelatedEnd)Column.EntityTypeProperty.GetValue(e.Entity, null);
     
                // In Edit mode, make sure it's loaded (doesn't make sense in Insert mode)
                if (Mode == DataBoundControlMode.Edit && !entityCollection.IsLoaded)
                {
                    entityCollection.Load();
                }
     
                // Get an IList from it (i.e. the list of territories for the current employee)
                // REVIEW: we should be using EntityCollection directly, but EF doesn't have a
                // non generic type for it. They will add this in vnext
                IList entityList = ((IListSource)entityCollection).GetList();
     
                // Go through all the territories (not just those for this employee)
                foreach (object childEntity in childTable.GetQuery(e.Context))
                {
     
                    // Check if the employee currently has this territory
                    bool isCurrentlyInList = entityList.Contains(childEntity);
     
                    // Find the checkbox for this territory, which gives us the new state
                    string pkString = childTable.GetPrimaryKeyString(childEntity);
                    //ListItem listItem = CheckBoxList1.Items.FindByValue(pkString);
                    TreeListDataItem radItem = null;
     
                    foreach (TreeListDataItem item in _radTreeList1.Items)
                    {
                        if (item["Id"].Text == pkString)
                            radItem = item;
                    }
     
                    if (radItem == null)
                        continue;
     
                    // If the states differs, make the appropriate add/remove change
                    if (radItem.Selected)
                    {
                        if (!isCurrentlyInList)
                            entityList.Add(childEntity);
                    }
                    else
                    {
                        if (isCurrentlyInList)
                            entityList.Remove(childEntity);
                    }
                }
            }
     
            protected void DataBound()
            {
                MetaTable childTable = ChildrenColumn.ChildTable;
     
                // Comments assume employee/territory for illustration, but the code is generic
     
                IList entityList = null;
                ObjectContext objectContext = null;
     
                if (Mode == DataBoundControlMode.Edit)
                {
                    object entity;
                    ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor;
                    if (rowDescriptor != null)
                    {
                        // Get the real entity from the wrapper
                        entity = rowDescriptor.GetPropertyOwner(null);
                    }
                    else
                    {
                        entity = Row;
                    }
     
                    // Get the collection of territories for this employee and make sure it's loaded
                    RelatedEnd entityCollection = Column.EntityTypeProperty.GetValue(entity, null) as RelatedEnd;
                    if (entityCollection == null)
                    {
                        throw new InvalidOperationException(String.Format("The ManyToMany template does not support the collection type of the '{0}' column on the '{1}' table.", Column.Name, Table.Name));
                    }
                    if (!entityCollection.IsLoaded)
                    {
                        entityCollection.Load();
                    }
     
                    // Get an IList from it (i.e. the list of territories for the current employee)
                    // REVIEW: we should be using EntityCollection directly, but EF doesn't have a
                    // non generic type for it. They will add this in vnext
                    entityList = ((IListSource)entityCollection).GetList();
     
                    // Get the current ObjectContext
                    // REVIEW: this is quite a dirty way of doing this. Look for better alternative
                    ObjectQuery objectQuery = (ObjectQuery)entityCollection.GetType().GetMethod(
                        "CreateSourceQuery").Invoke(entityCollection, null);
                    objectContext = objectQuery.Context;
                }
     
                // Go through all the territories (not just those for this employee)
                foreach (object childEntity in childTable.GetQuery(objectContext))
                {
                    _radTreeList1.DataKeyNames = new string[] { "Id" };
                    _radTreeList1.ParentDataKeyNames = new string[] { "ParentId" };
     
                    TreeListDataItem radItem = new TreeListDataItem(_radTreeList1
                        , TreeListItemType.Item
                        , _radTreeList1.Items.Count
                        , true);
     
                    radItem.DataItem = childEntity;
     
                    // Make it selected if the current employee has that territory
                    if (Mode == DataBoundControlMode.Edit)
                    {
                        radItem.Selected = entityList.Contains(childEntity);
                    }
     
                    _radTreeList1.Items.Add(radItem);
                }
            }
     
            protected void _radTreeList1_DataBound(object sender, EventArgs e)
            {
                DataBound();
            }
     
            public override Control DataControl
            {
                get
                {
                    return _radTreeList1;
                }
            }
     
        }
    }
  2. Scott Morrison
    Scott Morrison avatar
    4 posts
    Member since:
    Oct 2007

    Posted 27 Jun 2011 Link to this post

    I've got it all working using a different approach. Very nice. Not very generic at this point but it would not take much to make it so. The solution is below.

    I would still like to know why the first approach threw the error. Anyone have any ideas?

    Here is the working solution using a different approach.

    <%@ Control Language="C#" CodeBehind="ManyToMany_Edit.ascx.cs" Inherits="Ctp.U.Web.ResidentPortal.Forms.ManyToMany_EditField" %>
    <%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
     
    <telerik:RadAjaxPanel ID="_radAjaxPanel" runat="server">
        <telerik:RadTreeList ID="_radTreeList" runat="server" DataKeyNames="Id" ParentDataKeyNames="ParentId"
            AutoGenerateColumns="False" DataSourceID="_entityDataSource" AllowMultiItemSelection="True"
            AllowMultiItemEdit="False" Culture="(Default)" IsItemInserted="False" AllowRecursiveSelection="true"
            OnDataBound="_radTreeList_DataBound" OnItemCommand="_radTreeList_ItemCommand" >
            <ClientSettings AllowPostBackOnItemClick="true"></ClientSettings>
            <ValidationSettings CommandsToValidate="PerformInsert,Update" />
            <Columns>
                <telerik:TreeListBoundColumn DataField="Name" HeaderText="Name"
                    UniqueName="Name">
                </telerik:TreeListBoundColumn>
                <telerik:TreeListSelectColumn HeaderStyle-Width="50px">
                </telerik:TreeListSelectColumn>
            </Columns>
        </telerik:RadTreeList>
    </telerik:RadAjaxPanel>
    <asp:EntityDataSource ID="_entityDataSource" runat="server" ConnectionString="name=WashUResidentPortalEntities"
        DefaultContainerName="WashUResidentPortalEntities" EnableFlattening="False" EntitySetName="Organizations">
    </asp:EntityDataSource>

     

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data.Objects.DataClasses;
    using System.Web.DynamicData;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Web.UI;
     
    namespace Ctp.U.Web.ResidentPortal.Forms
    {
        public partial class ManyToMany_EditField : System.Web.DynamicData.FieldTemplateUserControl
        {
            List<string> _selectedKeys;
     
            public void Page_Load(object sender, EventArgs e)
            {
                // Register for the DataSource's updating event
                EntityDataSource ds = (EntityDataSource)this.FindDataSourceControl();
     
                // This field template is used both for Editing and Inserting
                ds.Updating += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting);
                ds.Inserting += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting);
     
                if (string.IsNullOrEmpty(_entityDataSource.EntitySetName))
                    _entityDataSource.EntitySetName = ChildrenColumn.ChildTable.Name;
                _selectedKeys = ViewState["_selectedKeys"] as List<string>;
            }
     
            protected void _radTreeList_ItemCommand(object sender, TreeListCommandEventArgs e)
            {
                switch (e.CommandName)
                {
                    case "ExpandCollapse":
                        break;
                    case "Deselect":
                        e.ExecuteCommand(sender);
                        RemoveSelections();
                        break;
                    case "Select":
                        e.ExecuteCommand(sender);
                        break;
                    case "ItemClick":
                        e.ExecuteCommand(sender);
                        TreeListDataItem item = e.Item as TreeListDataItem;
                        item.Selected = !item.Selected;
                        RemoveSelections();
                        break;
                }
            }
     
            protected void _radTreeList_DataBound(object sender, EventArgs e)
            {
                DataBound();
            }
     
            protected override void OnDataBinding(EventArgs e)
            {
                base.OnDataBinding(e);
                GetSelections();
                _radTreeList.ExpandAllItems();
            }
     
            void DataSource_UpdatingOrInserting(object sender, EntityDataSourceChangingEventArgs e)
            {
                MetaTable childTable = ChildrenColumn.ChildTable;
     
                // Comments assume employee/territory for illustration, but the code is generic
     
                // Get the collection of territories for this employee
                RelatedEnd entityCollection = (RelatedEnd)Column.EntityTypeProperty.GetValue(e.Entity, null);
     
                // In Edit mode, make sure it's loaded (doesn't make sense in Insert mode)
                if (Mode == DataBoundControlMode.Edit && !entityCollection.IsLoaded)
                {
                    entityCollection.Load();
                }
     
                // Get an IList from it (i.e. the list of territories for the current employee)
                // REVIEW: we should be using EntityCollection directly, but EF doesn't have a
                // non generic type for it. They will add this in vnext
                IList entityList = ((IListSource)entityCollection).GetList();
     
                // Go through all the territories (not just those for this employee)
                foreach (object childEntity in childTable.GetQuery(e.Context))
                {
     
                    // Check if the employee currently has this territory
                    bool isCurrentlyInList = entityList.Contains(childEntity);
     
                    // Find the checkbox for this territory, which gives us the new state
                    string pkString = childTable.GetPrimaryKeyString(childEntity);
                    //ListItem listItem = CheckBoxList1.Items.FindByValue(pkString);
                    TreeListDataItem radItem = null;
     
                    foreach (TreeListDataItem item in _radTreeList.Items)
                    {
                        if (item.GetDataKeyValue(_radTreeList.DataKeyNames[0]).ToString() == pkString)
                        {
                            radItem = item;
                            break;
                        }
                    }
     
                    if (radItem == null)
                        continue;
     
                    // If the states differs, make the appropriate add/remove change
                    if (radItem.Selected)
                    {
                        if (!isCurrentlyInList)
                            entityList.Add(childEntity);
                    }
                    else
                    {
                        if (isCurrentlyInList)
                            entityList.Remove(childEntity);
                    }
                }
            }
     
            void GetSelections()
            {
                if (Mode == DataBoundControlMode.Edit && _selectedKeys == null)
                {
                    MetaTable childTable = ChildrenColumn.ChildTable;
                    IList entityList;
                    // Comments assume employee/territory for illustration, but the code is generic
     
                    object entity;
                    ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor;
                    if (rowDescriptor != null)
                    {
                        // Get the real entity from the wrapper
                        entity = rowDescriptor.GetPropertyOwner(null);
                    }
                    else
                    {
                        entity = Row;
                    }
     
                    // Get the collection of territories for this employee and make sure it's loaded
                    RelatedEnd entityCollection = Column.EntityTypeProperty.GetValue(entity, null) as RelatedEnd;
                    if (entityCollection == null)
                    {
                        throw new InvalidOperationException(String.Format("The ManyToMany template does not support the collection type of the '{0}' column on the '{1}' table.", Column.Name, Table.Name));
                    }
                    if (!entityCollection.IsLoaded)
                    {
                        entityCollection.Load();
                    }
     
                    // Get an IList from it (i.e. the list of territories for the current employee)
                    // REVIEW: we should be using EntityCollection directly, but EF doesn't have a
                    // non generic type for it. They will add this in vnext
                    entityList = ((IListSource)entityCollection).GetList();
                    _selectedKeys = new List<string>();
                    foreach (object selected in entityList)
                        _selectedKeys.Add((selected as System.Data.Objects.DataClasses.EntityObject).EntityKey.EntityKeyValues[0].Value.ToString());
                    ViewState["_selectedKeys"] = _selectedKeys;
                }
            }
     
            protected void DataBound()
            {
                if (Mode == DataBoundControlMode.Edit && _selectedKeys.Count > 0)
                {
                    foreach (TreeListDataItem radItem in _radTreeList.Items)
                    {
                        if (_selectedKeys.Contains(GetRadTreeItemKey(radItem)))
                            radItem.Selected = true;
                    }
                }
            }
     
            void RemoveSelections()
            {
                if (Mode == DataBoundControlMode.Edit && _selectedKeys.Count > 0)
                {
                    foreach (TreeListDataItem radItem in _radTreeList.Items)
                    {
                        if (!radItem.Selected)
                            _selectedKeys.Remove(GetRadTreeItemKey(radItem));
                    }
                }
            }
     
            string GetRadTreeItemKey(TreeListDataItem radItem)
            {
                return radItem.GetDataKeyValue(_radTreeList.DataKeyNames[0]).ToString();
            }
     
            public override Control DataControl
            {
                get
                {
                    return _radTreeList;
                }
            }
     
        }
    }
  3. UI for ASP.NET Ajax is Ready for VS 2017
  4. Radoslav
    Admin
    Radoslav avatar
    1564 posts

    Posted 30 Jun 2011 Link to this post

    Hi Scott,

    I am glad that you achieved the desired functionality. Additionally please keep in mind that RadTreeList does not support directly adding items into its Items collection. The RadTreeList is a bound controls and to be properly bound it needs a collection of data items to be passed to its DataSource property or DataSource control. More information you could find on the following resources:
    http://www.telerik.com/help/aspnet-ajax/treelist-simple-vs-advanced-binding.html
    http://www.telerik.com/help/aspnet-ajax/treelist-net35-data-source-controls.html

    Best wishes,
    Radoslav
    the Telerik team

    Browse the vast support resources we have to jump start 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.

Back to Top