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

LoadOnDemand Self Referencing Scenario

3 Answers 162 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Jeff
Top achievements
Rank 1
Jeff asked on 11 Feb 2009, 04:08 PM
Hello,

I have a treeview that should display the contents of my self-referencing table in the database.  This table contains a list of hierarchical status types.  This table has primary key and parentID columns. I am using ADO.NET data services to get the data. I modeled the code similar to your on-demand example .

In the initial data query used to load the treeview item source, I used expand methods to get the child items of the root nodes and that works fine (I can navigate to the next level).  If the user attempts to expand a child node, I get the item, convert it to the cooresponding object and then call the Context.BeginLoadProperty to load the related items for this entity.  The relation used to populate the items is called "ChildCategories".

I can see in the async completed event that the current node's child relation has the correct number of child objects, but the treeview control displays a circular icon and does not display the child nodes.  Does the tree view control need to be refreshed?

Almost there, but must be missing something.  Please inspect the following.

Thanks in advance for your help.
Jeff

<telerik:RadPage x:Class="SilverlightClient.StatusTypeLookupPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:ms="clr-namespace:System.Windows.Controls;assembly=System.Windows" 
    xmlns:toolkit="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls" 
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
    xmlns:blacklight="clr-namespace:Blacklight.Silverlight.Controls;assembly=Blacklight.Silverlight.Controls" 
    xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" 
    xmlns:telerikDocking="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking" 
    xmlns:telerikInput="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input" 
    xmlns:telerikMedia="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.MediaPlayer" 
    xmlns:telerikNavigation="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"     
    xmlns:telerikGrid="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView" 
    xmlns:telerikGridView="clr-namespace:Telerik.Windows.Controls.GridView;assembly=Telerik.Windows.Controls.GridView" 
    xmlns:xapWorksModel="clr-namespace:SilverlightClient.xapWorksEntities" 
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" 
    xmlns:local="clr-namespace:SilverlightClient" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
    Width="Auto" Height="Auto" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800" Margin="0,0,0,0">  
 
    <telerik:RadPage.Resources> 
 
        <DataTemplate x:Key="ItemTemplate">  
            <TextBlock Text="{Binding Description}" /> 
        </DataTemplate> 
 
        <telerik:HierarchicalDataTemplate   
            x:Key="CategoryTemplate"   
            ItemsSource="{Binding ChildCategories}"   
            ItemTemplate="{StaticResource ItemTemplate}">  
            <TextBlock Text="{Binding Description}"/>  
 
        </telerik:HierarchicalDataTemplate> 
          
 
    </telerik:RadPage.Resources> 
 
    <Grid x:Name="LayoutRoot" Background="{StaticResource PanelBackground3}" HorizontalAlignment="Stretch" Margin="0,25,0,0">  
        <Grid.RowDefinitions> 
            <!-- the contact buttons goes here --> 
            <RowDefinition Height="Auto"/>  
            <!-- the main content goes here --> 
            <RowDefinition Height="*"/>  
        </Grid.RowDefinitions> 
 
        <Grid.ColumnDefinitions> 
            <ColumnDefinition Width="*" /> 
        </Grid.ColumnDefinitions> 
 
        <!-- create the page title --> 
        <StackPanel Orientation="Horizontal" Grid.Row="0" HorizontalAlignment="Center">  
            <TextBlock Text="Status Type Category Manager" FontSize="30" Foreground="{StaticResource Wheat}"/>  
        </StackPanel> 
 
        <!-- create a stackpanel with 2 columns (one for the treeview containing the StatusTypeCategory(s), the other for the StatusTypes --> 
 
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" 
            Grid.Row="1">  
 
            <!-- the first panel --> 
            <StackPanel Orientation="Vertical" Width="400"  VerticalAlignment="Stretch" Margin="20,20,20,20">  
                <TextBlock Text="Status Type Categories" FontSize="16" Foreground="#FFFFFFFF" /> 
 
                <telerikNavigation:RadTreeView x:Name="RadTreeView1" Margin="0,30,0,0" 
                    Background="WhiteSmoke" 
                    MinHeight="500" 
                    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   
                    IsOptionElementsEnabled="true" 
                    IsTriStateMode="true" IsLineEnabled="True" SelectionMode="Extended" 
                    IsEditable="True"   
                    IsLoadOnDemandEnabled="True"   
                    LoadOnDemand="RadTreeView1_LoadOnDemand" 
                    IsDragDropEnabled="True" ItemsOptionListType="CheckList" 
                    ImagesBaseDir="/Examples/Common/Images/VistaIcons/" 
                    ItemTemplate="{StaticResource CategoryTemplate}"   
                    Loaded="RadTreeView1_Loaded" /> 
 
                <!-- define some buttons for CRUD operations --> 
                <StackPanel Orientation="Horizontal" Grid.Row="0" HorizontalAlignment="Center" Margin="0,20,0,10">  
                    <Button x:Name="NewCategoryButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                        <ContentPresenter> 
                            <StackPanel Orientation="Vertical">  
                                <Image Source="../Images/VistaCommon/new_document_lined_24.png" Height="24"/>  
                                <TextBlock Text="New"/>  
                            </StackPanel> 
                        </ContentPresenter> 
                    </Button> 
 
                    <Button x:Name="AddCategoryButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                        <ContentPresenter> 
                            <StackPanel Orientation="Vertical">  
                                <Image Source="../Images/VistaCommon/new_document_lined_24.png" Height="24"/>  
                                <TextBlock Text="Add"/>  
                            </StackPanel> 
                        </ContentPresenter> 
                    </Button> 
 
                    <Button x:Name="AddSubCategoryButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                        <ContentPresenter> 
                            <StackPanel Orientation="Vertical">  
                                <Image Source="../Images/VistaCommon/new_document_lined_24.png" Height="24"/>  
                                <TextBlock Text="Sub"/>  
                            </StackPanel> 
                        </ContentPresenter> 
                    </Button> 
 
                    <Button x:Name="EditCategoryButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                        <ContentPresenter> 
                            <StackPanel Orientation="Vertical">  
                                <Image Source="../Images/VistaCoreI/edit_24.png" Height="24"/>  
                                <TextBlock Text="Edit"/>  
                            </StackPanel> 
                        </ContentPresenter> 
                    </Button> 
 
                    <Button x:Name="SaveCategoryButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                        <ContentPresenter> 
                            <StackPanel Orientation="Vertical">  
                                <Image Source="../Images/VistaCommon/save_24.png" Height="24"/>  
                                <TextBlock Text="Save"/>  
                            </StackPanel> 
                        </ContentPresenter> 
                    </Button> 
 
                    <Button x:Name="DeleteCategoryButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                        <ContentPresenter> 
                            <StackPanel Orientation="Vertical">  
                                <Image Source="../Images/VistaCommon/delete_24.png" Height="24"/>  
                                <TextBlock Text="Delete"/>  
                            </StackPanel> 
                        </ContentPresenter> 
                    </Button> 
                </StackPanel> 
            </StackPanel> 
 
    <!-- the second panel --> 
                <StackPanel Orientation="Vertical" Width="400" Margin="60,20,20,20">  
                    <TextBlock Text="Status Types" FontSize="16" Foreground="#FFFFFFFF"/>  
 
                    <!-- this is the status type grid --> 
                    <telerikGrid:RadGridView x:Name="StatusTypeGrid" Grid.Row="1" MinHeight="500" Margin="0,30,0,0" 
                                 AutoGenerateColumns="False" 
                                 ShowGroupPanel="False" > 
                        <telerikGrid:RadGridView.Columns> 
                            <telerikGridView:GridViewDataColumn  
                            HeaderText="Status Type" 
                            UniqueName="Description" 
                            Width="400"/>  
 
                        </telerikGrid:RadGridView.Columns> 
                    </telerikGrid:RadGridView> 
 
                    <!-- define some buttons for CRUD operations --> 
                    <StackPanel Orientation="Horizontal" Grid.Row="0" HorizontalAlignment="Center" Margin="0,20,0,10">  
                        <Button x:Name="NewStatusTypeButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                            <ContentPresenter> 
                                <StackPanel Orientation="Vertical">  
                                    <Image Source="../Images/VistaCommon/new_document_lined_24.png" Height="24"/>  
                                    <TextBlock Text="New"/>  
                                </StackPanel> 
                            </ContentPresenter> 
                        </Button> 
 
                        <Button x:Name="EditStatusTypeButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                            <ContentPresenter> 
                                <StackPanel Orientation="Vertical">  
                                    <Image Source="../Images/VistaCoreI/edit_24.png" Height="24"/>  
                                    <TextBlock Text="Edit"/>  
                                </StackPanel> 
                            </ContentPresenter> 
                        </Button> 
 
                        <Button x:Name="SaveStatusTypeButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                            <ContentPresenter> 
                                <StackPanel Orientation="Vertical">  
                                    <Image Source="../Images/VistaCommon/save_24.png" Height="24"/>  
                                    <TextBlock Text="Save"/>  
                                </StackPanel> 
                            </ContentPresenter> 
                        </Button> 
 
                        <Button x:Name="DeleteStatusTypeButton"  Height="Auto" Width="50" Margin="5,0,0,0" > 
                            <ContentPresenter> 
                                <StackPanel Orientation="Vertical">  
                                    <Image Source="../Images/VistaCommon/delete_24.png" Height="24"/>  
                                    <TextBlock Text="Delete"/>  
                                </StackPanel> 
                            </ContentPresenter> 
                        </Button> 
                    </StackPanel> 
                </StackPanel> 
                  
            </StackPanel> 
 
    </Grid> 
</telerik:RadPage> 
 

Here is the code behind
Imports System  
Imports System.ComponentModel  
Imports System.Collections.Generic  
Imports System.Collections.ObjectModel  
Imports System.Linq  
Imports System.Net  
Imports System.Windows  
Imports System.Windows.Controls  
Imports System.Windows.Data  
Imports System.Windows.Documents  
Imports System.Windows.Input  
Imports System.Windows.Media  
Imports System.Windows.Media.Animation  
Imports System.Windows.Shapes  
 
Imports Blacklight  
 
Imports Telerik.Windows  
Imports Telerik.Windows.Controls  
Imports Telerik.Windows.Controls.Docking  
Imports Telerik.Windows.Controls.GridView  
Imports Telerik.Windows.Controls.GridView.Rows  
Imports Telerik.Windows.Input  
Imports Telerik.Windows.Controls.MediaPlayer  
Imports Telerik.Windows.Controls.Navigation  
 
 
Imports System.Data.Services.Client  
Imports SilverlightClient.xapWorksEntities  
 
 
Partial Public Class StatusTypeLookupPage  
    Inherits Telerik.Windows.Controls.RadPage
#Region "Variable Declarations"  
 
    ' create a collection to hold the StatusTypeCategory items  
    Dim theStatusTypeCategories As New ObservableCollection(Of StatusTypeCategory)  
 
    ' create a collection to hold the child StatusTypeCategory items  
    ' we will dynamically load the children when a StatusTypeCategory node is selected  
    Dim theStatusTypeCategoryChildren As New ObservableCollection(Of StatusTypeCategory)  
 
    ' create a variable to hold the current treeviewitem  
    Dim theTreeViewItem As RadTreeViewItem  
 
    ' create a variable to thold the current StatusTypeCategory  
    Dim theCategory As StatusTypeCategory
#End Region  
 
    Public Sub New()  
        InitializeComponent()  
 
    End Sub 
 
    Private Sub RadTreeView1_Loaded(ByVal sender As System.ObjectByVal e As System.Windows.RoutedEventArgs)  
        RadTreeView1.ItemsSource = App.theStatusTypeCategoryCollection  
    End Sub 
 
 
    Private Sub RadTreeView1_LoadOnDemand(ByVal sender As System.ObjectByVal e As Telerik.Windows.RadRoutedEventArgs)  
        theTreeViewItem = TryCast(e.OriginalSource, RadTreeViewItem)  
 
        theCategory = TryCast(theTreeViewItem.Item, StatusTypeCategory)  
 
        If theCategory IsNot Nothing Then 
            ' get the child category's asyncronously  
            LoadStatusTypeCategoryChildData(theCategory)  
        Else 
            theTreeViewItem.IsLoadOnDemandEnabled = False 
        End If 
 
    End Sub 
 
    Private Sub LoadStatusTypeCategoryChildData(ByVal theCategory As StatusTypeCategory)  
     
        App.theService.BeginLoadProperty(theCategory, _  
                           "ChildCategories", _  
                           New AsyncCallback(AddressOf LoadRelatedChildItemsComplete), _  
                           theCategory)  
 
    End Sub 
 
    Private Sub LoadRelatedChildItemsComplete(ByVal result As IAsyncResult)  
        App.theService.EndLoadProperty(result)  
 
        If theCategory.ChildCategories.Count.Equals(0) Then 
            ' if we have no items, then set the control indicator  
            theTreeViewItem.IsLoadOnDemandEnabled = False 
 
            ' refresh the treeview control  
        End If 
 
    End Sub 
 
 
    Private Sub RadTreeView1_ItemPrepared(ByVal sender As System.ObjectByVal e As Telerik.Windows.Controls.RadTreeViewItemPreparedEventArgs)  
 
    End Sub 
End Class 
 

3 Answers, 1 is accepted

Sort by
0
Miroslav
Telerik team
answered on 16 Feb 2009, 12:43 PM
Hi Jeff,

First, sorry for the delayed reply!

I can think of several reasons for the current behavior:

1. The ItemTemplate of the HierarchicalDataTemplate is set to a DataTemplate. The template is normally inherited and you do not need to explicitly set the child template of the item (since it will be the same). Also, setting it  to a non-hierarchical template will mean that it can have only 1 level of nesting. Remove the ItemTemplate there.

2. The ChildCategories should be an ObservableCollection.

3. If initially the ChildCategories property is null, then the StatusTypeCategory has to implement the INotifyPropertyChnaged, and the event should be raised when the new ChildCategories properties is set.

Does this help you?

Sincerely yours,
Miroslav
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
0
Jeff
Top achievements
Rank 1
answered on 16 Feb 2009, 06:12 PM
Hello Miroslav,

Per you suggestions, I removed the item template.  In the generated data service client code, I replaced all occurances of System.Collections.ObjectModel.Collection with System.Collections.ObjectModel.ObservableCollection.  I took a look at your Northwind LoadOnDemand example and created a partial class to implement the iNotifyPropertyChanged behavior as follows:

Imports System.ComponentModel  
 
Namespace xapWorksEntities  
    Partial Public Class EntityCategoryType  
        Implements INotifyPropertyChanged  
        Private Sub OnEntityCategoryTypeIDChanged()  
            Me.OnPropertyChanged("EntityCategoryTypeID")  
        End Sub 
 
        Private Sub OnParentIDChanged()  
            Me.OnPropertyChanged("CategoryName")  
        End Sub 
 
        Private Sub OnDescriptionChanged()  
            Me.OnPropertyChanged("Description")  
        End Sub 
 
        Private Sub OnChildEntitiesChanged()  
            Me.OnPropertyChanged("ChildEntities")  
        End Sub
#Region "INotifyPropertyChanged Members"  
 
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged  
 
        Private Sub OnPropertyChanged(ByVal propertyName As String)  
            If Me.PropertyChangedEvent IsNot Nothing Then 
                RaiseEvent PropertyChanged(MeNew PropertyChangedEventArgs(propertyName))  
            End If 
        End Sub
#End Region  
    End Class 
End Namespace 
 

The table in the database is named "EntityCategoryType", and has columns "EntityCategoryTypeID", "ParentID" and "Description".  ParentID is related to EntityCategoryID and the relation named "ChildEntities" returns the collection of related EntityCategoryTypes.

After implementing the changes, this did not solve the problem previously described.  I am just getting started with ADO.Net, but this sure seems rather difficult and a bunch of work just to get basic crud operations working.  In my WinForms apps, I have previous used IdeaBlades' DevForce product which worked out really well.  I'm definitly going to be looking for an ORM solution that enables operating at a higher level of abstraction.

In any case, do you have any other idea's that I could investigate?

I also have another issue that I would like to illuminate.  When adding a new item to the datasouce, I am seeing the entity being added to the root node of the treeview.  This happens even when the new item is a child node.  I have set the relation and links as necessary and verified the data is correct as stored in the database after the save operation.  If the app is restarted, the treeview will show the new entity correctly in the treeview (as long as it is only a top level or second level node).  Here is the code used to add a new EntityCategoryType:

 Private Sub AddEntityCategoryTypeButton_Click() Handles AddEntityCategoryTypeButton.Click  
 
        ' make sure we have a EntityCategoryType selected  
        If IsNothing(theCurrentCategoryType) Then 
            Return 
        End If 
 
        ' create a new EntityCategoryType at the currently selected level  
        Dim aEntityCategoryType As EntityCategoryType = New EntityCategoryType  
        aEntityCategoryType.Description = "New Category" 
 
        ' set this category's parent relationship  
        aEntityCategoryType.ParentEntity = theCurrentCategoryType  
 
        Dim g As Guid  
        g = Guid.NewGuid()  
        aEntityCategoryType.EntityCategoryTypeID = g  
 
        ' add the new category to the data service context  
        App.theService.AddToEntityCategoryTypes(aEntityCategoryType)  
 
        ' create the association links  
        App.theService.AddLink(theCurrentCategoryType, "ChildEntities", aEntityCategoryType)  
        App.theService.SetLink(aEntityCategoryType, "ParentEntity", theCurrentCategoryType)  
 
        ' update the status  
        App.theLookupDataMainPage.txtStatus.Text = "Adding new EntityCategoryType" 
 
        ' save the service  
        App.theService.BeginSaveChanges(AddressOf AddEntityCategoryTypeDataCompleted, aEntityCategoryType)  
    End Sub 

In my code, I am expecting that the treeview control will display changes to the underlying datasource without having to explicitly add items to the treeview.  Do you have any explanation for why the item would should up but not in the correct hierarchy?

At the end of the day, it would be very helpful to have a example that demonstrates the type of scenario I'm trying to implement (self-referencing hierarchical data using ADO.Net, as I have already spent a week on this without resolution.

Thanks very much for your assistance.
Jeff
0
Miroslav
Telerik team
answered on 17 Feb 2009, 09:27 AM
Hello Jeff,

I did not see where the item is added to the parent's childrens list (ChildEntities), maybe this is why it does not appear in the TreeView.

I created a simple example (attached), which i stripped-down version of LoadOnDemand. The thing to note there is that the DataItems (EntityCategoryType) do not even have a ParentId, because it is not relevant for binding. In the sample project the async operation is simulated, but it will work for any async operation.

Generally, the WPF & Silverlight binding is best suited for the M-V-VM pattern where the View is bound to the ViewModel. The ViewModel closely resembles the View. In this case, the items need to be arranged in a hierarchy. The Id's and ParentIds that are sent around are just identifiers for the items, but the treeView will not build unless the items are added to the Children collection of the parent.

The Wpf / Silverlight databinding differs from other frameworks where the Control will build the hierarchy itself, but it is also quite powerful.

Hopefully this will help you,

Sincerely yours,
Miroslav
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
Tags
TreeView
Asked by
Jeff
Top achievements
Rank 1
Answers by
Miroslav
Telerik team
Jeff
Top achievements
Rank 1
Share this question
or