Any ideas on what could be causing this?
below is my xaml to start:
<telerik:RadTreeView Grid.Column="0" Grid.Row="1" PathSeparator="{Binding PathSeperator, Source=tbl_Path}"
ItemEditTemplate="{StaticResource EditTreeViewTemplate1}"
ItemsSource="{Binding Paths}"
SelectedValuePath="PathID" x:Name="FolderTreeView"
IsLoadOnDemandEnabled="True"
IsLineEnabled="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEditable="True"
Margin="2,2,2,2" Height="400" LoadOnDemand="FolderTreeView_LoadOnDemand">
<telerik:RadTreeView.ItemTemplate>
<telerik:HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image x:Name="Icon" Source="/Images/16x16/folder_closed.png" Width="16" Height="16" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Item.Name}" />
</StackPanel>
</telerik:HierarchicalDataTemplate>
</telerik:RadTreeView.ItemTemplate>
</telerik:RadTreeView>
15 Answers, 1 is accepted
We are not aware of such issue with the RadTreeView. And I also couldn't reproduce it. Perhaps the issue moght be caused by the logic inside the LoadOnDemand() event. If the data is taking too much time to load, that maigh be causing the behavior you described. If you can send us a code snippet we'll be able to further investigate the issue.
I also attached the project I used for testing. Please take a look at it and let me know if I am missing something or if your scenario implements a different approach.
Kind regards,
Tina Stancheva
the Telerik team
Figured out the problem. I have to manually set the IsExpanded property of the tree view item after the ObservableCollection is filled with its data in the code-behind.. Doesn't make any sense since I just clicked the node. This is a defect.
So to re-state. I click a node, the loading icon appears over the expander, the load-on-demand event fires, the data for that item is retrieved and the ObservableCollection is filled, the loading icon disappears and expander returns to normal, however the node itself does not expand to show the child nodes bound to the ObservableCollection. I must click the node again OR use the following code after the ObservableCollection has been filled in order for the node I clicked to expand to see the child nodes.
item.IsExpanded = true;
FolderTreeView.UpdateLayout();
In short, doing any of the following on an ObservableCollection will cause this double node click issue:
1. Clear()
2. RemoveAll() - extension method
3. "New" up your collection - MyCollection = new ObservableCollection<string>();
Thanks,
Joey
I compared my codes with demo, the difference is I used MVVM to bind with ViewModel, while demo sample codes just assign the List to the ItemsSource of RadTreeView. The first level items are hard-coded. Other level items come from WCF.
Joey, I used your walk around but didn't help.
Can any Telerik guy help?
Here is the sample application exhibiting the issue.
radtreeviewdoubleexpandissue.zip
Thank you all for the input & Joey for the sample project!
The main reason for the strange behavior is that we have not anticipated that the LoadOnDemand will be used twice for one and the same item.
But let me explain a little how the LoadOnDemand works:
After the user expands an item that has IsLoadOnDemandEnabled = true, the LoadOnDemand event is raised and IsLoadingOnDemand property is set to true (it controls the spinning arrows animation).
Then whenever the ItemsSource of the TreeViewItem is updated, its IsExpanded property is set to true, the IsLoadingOnDemand is set to false and the items appear.
Then:
- neweing up the items collection does not seem to work because the Children property is not observable, defining it like so will notify the TreeView accordingly:
set
{
_children = value;
this
.OnPropertyChanged(
new
PropertyChangedEventArgs(
"Children"
));
}
newing up the collection will have the effect of clearing the items collection or removing all items.
When the TreeViewItems children are removed / reset, it sets is IsExpadned property to false.
Then, as I mentioned, we did not expect that LoadOnDemand will be used for the same item and you get the double-expand / click bug.
If you stop the LoadOnDemand when the items are retrieved, the TreeViewItem will work as expected:
_vml.HomeViewModel.GetChildren(path);
item.IsLoadOnDemandEnabled =
false
;
Stopping LoadOnDemand when items are retrieved is a good idea if there are no children, since it will remove the arrow.
We can help by doing to things:
(1) Automatically set IsLoadOnDemandEnabled to false once items are updated. (like in the code above)
(2) Make sure loading on demand works the second+ time as well
Both have advantages & disadvantages - so which one would you prefer?
Do you need to fetch items twice?
@Joey - I updated your Telerik Points.
@All - Does setting IsLoadOnDemandEnabled to false after the children are retrieved help in your case?
All the best,
Miroslav
the Telerik team
I added the OnPropertyChanged event on the “set” and now a “new up” behaves itself.
To your point of setting IsLoadOnDemandEnabled to false. This does not correct the double expand behavior it rather prevents the end user from ever expanding that item again. That’s not what I want either.
We might be headed down the wrong track here without the “why”. The reason this started is because I wanted to refresh the tree view; from the root node(s) all the way down to all of the nodes that are currently expanded by the user. Simple as that. I thought the easiest way to refresh the tree view would be to call the Clear() method on the observable collection at the root node(s) and then re-populate the collections again from the database. The logic for how the application remembers which nodes are expanded is irrelevant here and I don’t want to pursue that. I just want to be able to call the Clear() method on my ObservableCollection without the double expand issue. If I’m going about this the wrong way let me know how else a tree view can be refreshed in the manner I’ve described. E.g. all nodes need to be refreshed from the database.
Thanks,
Joey
MVVM is used to bind my viewmodel to UI. So in the UI resource I put this viewmodel, which cause this viewmodel is initiated once. Also, in the constructor of UI, in order to use the methods of viewmodel, I initiate viewmodel, which cause viewmodel is initiated twice. After puting the breakpoint, I can see exactly what happened.
I am not sure why two initiations cause tree view must be clicked twice. But after removing the the second viewmodel initiation, using (this.resource["resouce"] as ResourceClass) to avoid the second initiation, everything works fine.
BTW, in the constructor of ViewModel, I load the first level of treeview. Maybe this cause the problem?
Yes, clearing / resetting the collections is one valid way to do it.
If you are using RIA services, requering for the same data should be enough since the RIA service context manages the relationships between the items quite well.
You are right that the details about what happens with the IsExpanded value are not what you intended to report but I digressed a little. We decided to collapse the TreeViewItem when its items are reset which means that the user will have to click just once to expand the item in your case.
All the best,
Miroslav
the Telerik team
I have a problem similar to that described above. Here is my setup:
- Silverlight RadTreeView bound to a collection in my custom ObjectExplorer class as follows:
public ObservableCollection<ObjectExplorerItem> Items { get; private set; }
An ObjectExplorerItem represents one item on the tree view. Each Item has a Label property amongst others, which is the text to be displayed.
- Each ObjectExplorerItem has a Children property defined as
public ObservableCollection<ObjectExplorerItem> Children { get; set; } - Hierarchical data binding declaration as follows:
<telerik:HierarchicalDataTemplate x:Key="ClusterTreeItemTemplate" ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Label}"/> </telerik:HierarchicalDataTemplate> - My TreeView is declared with the following properties:
ItemTemplate="{StaticResource ClusterTreeItemTemplate}"
ItemsSource="{Binding ObjectExplorer.Items}" - Some of my nodes load on demand. Here is an example of the code that does this:
void StoresContainerLoadChildren(ObjectExplorerItem treeItem)
{
// simplified for the sake of example - this actually calls through to a DB
var stores = new[] { "Store 1", "Store 2", "Store 3" };
treeItem.Children.Clear();
foreach (var store in stores)
{
var storeItem = new ObjectExplorerItem(treeItem)
{
Kind = ObjectExplorerItemKind.Store,
Label = store
};
treeItem.Children.Add(storeItem);
}
}
As described above, when the user elects to expand the node, the data loads on demand correctly, but the node remains unexpanded. The user must attempt to expand the node for a second time to see the child nodes that were loaded on demand.
I have read through the comments and suggestions above, but I can't see how any of the solutions can be used in my situation.
If I remove the call to treeItem.Children.Clear() then the node expands straight away as expected. However, I also have a Refresh option, which needs to clear the old items before loading refreshed ones, so I can't just remove the call.
It was mentioned that the OnPropertyChanged event should be raised on the Children property in the setter for the children. I tried this as follows:
ObservableCollection<ObjectExplorerItem> _children = new ObservableCollection<ObjectExplorerItem>();
public ObservableCollection<ObjectExplorerItem> Children
{
get
{
return _children;
}
set
{
_children = value;
RaisePropertyChanged("Children");
}
} }
Firstly, this doesn't solve the problem, and secondly, it shouldn't even be necessary because the Children property is already observable without having to manually raise the changed event.
As suggested by Joey, I would like to try to implement the following code after each load on demand:
item.IsExpanded = true;
FolderTreeView.UpdateLayout();
However, remember that I am using MVVM, so all communication to the view must be done through bindings. The code that is retrieving the on-demand data is in my View Model, so I don't even have a reference to my RadTreeView control in this file to call its UpdateLayout() (this is the correct way to do things in MVVM). I also don't have access to any of the tree view items (only my ObjectExplorerItems), so I can't set the IsExpanded property.
I don't really understand the explanation from Telerik as to why this happens, and I don't really care - I just want it to work. But at this point I am completely stuck, and I have a broken Tree View that I am about to abandon for a different vendor's control that can cope with what I am trying to do. Unless, that is, someone can come up with a working solution soon?
Yours hopefully,
Mike
You can work around this defect with my fix by using the following lines of code after an item's load-on-demand event has finished:
item.IsExpanded = true;
FolderTreeView.UpdateLayout();
Since your using MVVM all you need to do is one of the following:
- Use your MVVM framework to issue a message back to the view and put the above code in that "callback".
- Simply create your own public event in the ViewModel and attach to it in the View's constructor and use the above code in the callback.
This is the ViewModel's method for load-on-demand and I fire a message to any one listening that the event has completed. Notice that I'm still calling the Clear() method on my ObservableCollection which is the initiator of this defect.
private void FolderTreeViewLoadOnDemand(tbl_Path expandingItem)
{
MyService.GetChildPaths(
new Action<IEnumerable<tbl_Path>, Exception>((a, b) =>
{
HeirarchicalPath currentItem =
this.Paths.Traverse(x => x.Children).Where(x => x.Item.FullPath == expandingItem.FullPath).FirstOrDefault();
if (currentItem != null)
{
currentItem.Children.Clear();
foreach (tbl_Path path in a)
currentItem.Children.Add(
new HeirarchicalPath(path));
}
// notify view that this is done
Messenger.Default.Send<NotificationMessage>(new NotificationMessage(currentItem, "GetChildPathsComplete"));
}), expandingItem.PathID);
}
Then in the view I simply run the following code any time that message comes back. I will most likely re-write this to use concrete events because I cringe at string based comparison which is not compile time enforced. This was just the simplest way to get it done while I was learning.
public FolderExplorerView()
{
InitializeComponent();
// register for view model notifications
Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
if (nm.Notification == "GetChildPathsComplete")
{
HeirarchicalPath path = nm.Sender as HeirarchicalPath;
RadTreeViewItem viewItem = FolderTreeView.GetTreeViewItem(path);
// remove the load-on-demand cabability for the UI item if the data item has no children
if (path.Children.Count == 0)
{
viewItem.IsLoadOnDemandEnabled =
false;
}
// expand the current item
viewItem.IsExpanded =
true;
FolderTreeView.UpdateLayout();
}
});
}
I'm using the MVVM light toolkit and this is how I'm currently doing it:Mr. 8 ball says "Outlook not so good"
Slightly out of context - are you binding the LoadOnDemand event directly to the viewmodel, if so could to post that syntax, I am only using the simplest MVVM light command binding and it needs expanding.
LoadOnDemand="{Binding LoadNode}"
Mark, find the code below. I'm also using Unity 2.0 for Silverlight so the view model gets passed into my constructor of my view and then execute a RelayCommand during the load-on-demand event.
public
partial class ContentExplorerView : UserControl
{
private
ContentExplorerViewModel _viewModel;
public ContentExplorerView(IUnityContainer container, ContentExplorerViewModel viewModel)
{
_viewModel = viewModel;
InitializeComponent();
}
private
void radTreeView1_LoadOnDemand(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
RadTreeViewItem clickedItem = (RadTreeViewItem)e.OriginalSource;
_viewModel.NavigationTreeViewLoadOnDemandCommand.Execute(clickedItem.Item);
}
}