This is a migrated thread and some comments may be shown as answers.

RadTreeList in the DynamicData ManyToMany_Edit.ascx Control

2 Answers 102 Views
TreeList
This is a migrated thread and some comments may be shown as answers.
Scott Morrison
Top achievements
Rank 1
Scott Morrison asked on 27 Jun 2011, 07:07 AM
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 Answers, 1 is accepted

Sort by
0
Scott Morrison
Top achievements
Rank 1
answered on 27 Jun 2011, 11:16 PM

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;
            }
        }
 
    }
}
0
Radoslav
Telerik team
answered on 30 Jun 2011, 08:35 AM
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.

Tags
TreeList
Asked by
Scott Morrison
Top achievements
Rank 1
Answers by
Scott Morrison
Top achievements
Rank 1
Radoslav
Telerik team
Share this question
or