I have a virtualized TreeView and am successfully using the mechanism described here to select a given domain object in the view (expanding the path to the object accordingly), but the tree is always realized fully, which is prohibitively slow for my case (several thousand items).
Is there a better way of getting from the object in a domain hierarchy to the tree
item instead of this brute force method? I know the path to the item in the domain hierarchy, so I would like to realize only those items that lie on the path to the item to be selected, but I have not found out how to do so.
A method I've used in the past is to also include an IsExpanded property binding to a ViewModel object (which represents your visual TreeItem).
You can then walk the path from your leaf node back to the root of the tree and setting IsExpanded to true as you go which means no brute force nasty non-MVVM compliant code in the code behind of your XAML to do so.
Related
My problem is the following: I got a Tree which has an dynamic depth of categories (each category can have sub-categories as well as entries).
Now I added a HierarchicalDataTemplate for the categories to display correctly in the TreeView. But I get a lot of empty entries, which do not apply the Template (wrong type) but show up in the tree as 'corpse'.
How can I ban them from the generation process? Because it's an abstract tree, they are of the same base-class as the categories are. So they get into the tree, because the tree always searches the "Branches"-property which contains either categories, entries or both.
Any ideas? I didn't find any event of the TreeView which probably give me the opportunity to skip various entries during generation nor any option/property of the template to do so.
Detailed Description: I got a generic Tree class. This class has branches of type "A_TreeLeaf" (abstract). The Tree's generic type must inherit A_TreeLeaf of course. My data is structured in categories (CategoryTreeLeaf) and Data (DataTreeLeaf). Each leaf can have sub-leaves (branches), of course.
Now I load my data from a database and build the tree. Each category has X sub-categories. And each category also could contain some Data. This structure helps me a lot, because I got an clear hierarchic structure of categories and data. This way it should be visualized to the user. But I want to separate Data and Categories. The TreeView should show just the categories (by an HierarchicalDataTemplate) and the ListView just the Data (by an DataTemplate). The ListView works fine, but the Tree shows some "corpse"-entries which are the DataTreeLeaf-instances.
I want to filter the DataTreeLeafs on generation or just stop the TreeView displaying them. Is there any "non-hack" solution? I don't want to copy the tree and remove the Data-leaves unless it's really necessary... because this would cause a lot of overhead work to do for me and to manage either the code behind which uses the real tree or the visualization with the fake-tree (because I need to bridge it somehow that it's updated automatically when one of both changes).
You have a unique problem... you have some data items in your hierarchical data that you don't want to display, but for some reason can't remove. If that sums up your problem, then you're doing something wrong.
In WPF, you shouldn't need to hide data items from the UI, instead you simply don't put them into the collection in the first place. It sounds like your process of filling your hierarchical data is flawed and you'd be better off fixing that at the source than trying to deal with the problems that it causes in the UI.
If you can't fix the actual process for whatever reason, then your next best option is to iterate through the data before you display it and simply remove any data elements that shouldn't be there. When using WPF, it is always best to provide your UI with data that fits the purpose.
However, if for whatever reason you can't even do that, then your last option is to simply define an additional DataTemplate for your abstract base class and just leave it empty:
<DataTemplate DataType="{x:Type YourDataTypesPrefix:YourBaseClass}">
</DataTemplate>
Of course, you'd have to define DataTemplates for each sub type, or they'd also be rendered empty.
In my application as soon as an object is dropped on canvas I stored it in a tree. Now, I'm trying to write a method to delete an object which is fine but I also need to delete that specific object from the tree. Each object has a unique id so this way I can find it in tree and remove it from tree while being deleted from the canvas.
In order to find a node in tree I have planned to store ID in each node (item.Tag), however, I'm facing two problems:
How can I access to details of a node from a different class? myTreeObj.Items.Tag doesn't work properly.
When I loop in tree myTreeObj.Items.Count shows more items that what I see.
Any comments will be appreciated.
Thanks.
In WPF, TreeViews are actually what they say they are: a view of a data structure. In WinForms, you had to crawl up and down the nodes of a TreeView and add them or remove them manually. In WPF, the proper approach is to add or remove items from the actual data hierarchy (to which the TreeView is binding) and use PropertyChanged or CollectionChanged notifications to tell the TreeView to update itself automatically.
What you are trying to do may be possible, but it is going to be an uphill fight all the way, and things will not work as expected. WPF REALLY wants you to use databinding, and any other approach is going to give you headaches.
This article may be a helpful place to start understanding how to work with the WPF TreeView:
http://joshsmithonwpf.wordpress.com/2008/05/24/the-wpf-treeview-is-a-view-of-a-tree/
EDIT:
The author's original article is actually more helpful:
http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode
I'm using WPF TreeView bound to my classes though MVVM to display a large array of multilevel data. Because of this, when I expand TreeNode, it takes some time and a lot of memory.
I think, that the memory requirement comes from TreeView scouting out nodes down the line, not just the first generation of the children, forcing those nodes to load data.
Is there any way to limit this "scouting" behavior to only the first generation of child nodes?
WPF generates TreeViewItem(s) only when they are displayed, i.e. no item is generated until its Parent expanded. So, I think the problem is not a "scouting".
Anyway, you may try to implement dynamic loading in your ViewModel by adding synchronization with TreeViewItem.IsExpanded using TwoWay Binding. Take a look at this. You may use the same solution for IsExpanded property. Don't forget to add fake item to tell TreeViewItem that it is expandable.
Check if TreeView is using VirtualizingStackPanel. I believe that it is used by default in .NET 4.0, but you must set it explicitly in previous .NET versions.
For more information, see How to: Improve the Performance of a TreeView.
I currently have a simple tree view that contains one parent node with multiple child nodes. I was wondering if there is a way to find the location of the selected node in the parent tree.
I currently have an action event on the treeview and when the user clicks on the child it prints out the string value of the selected child. I have tried using:
int val = TreeView.SelectedItemProperty.GlobalIndex;
but it always returns 0. I have seen some examples in VB but I cant seem to get the same idea to work in C#.
You have to use the ItemContainerGenerator property of the Treeview.
http://msdn.microsoft.com/en-us/library/system.windows.controls.itemcontainergenerator.aspx
See: ContainerFromIndex and IndexFromContainer
Note that each TreeViewItem also has an ItemContainerGenerator (its an ItemsControl), so you'd have to recursively search down the tree if you have multiple levels.
I think the answer to all your treeview problems (and most ui ones) in wpf is to build a ViewModel. Anytime you start crawling the visual tree to look for elements that you are already binding to, you are doing things the hard way. Once you start using ItemsContainerGenerator you have to start worrying about a whole lot of issues you should not have to.
You are binding to a hierarchical structure. If that structure has a selected item property on each item and it is bound to the TreeViewItem selected item then you can just get the selected item in code and do everything else from there. Have a look at a similiar question here.
So i didn't find the answer i was looking for (I may of confused others with what my question was. by saying location). Anyways how I solved it was I got the string value of the child selected and compared it to my list. Thanks to those who answered!
I have a TreeView that is bound to a collection class that I have no control over. Inside this class is a collection of objects, which each have their own collection of items. This hierarchy is fixed at 3 deep. The children of the TreeView are contained in an ObservableCollection and are updated in the TreeView accordingly. The collections inside each of these objects are not observable, and thus I have to manually re-bind the data to the TreeView each time I add an object to one of the children. This causes all of the expanded children to be reset to an unexpanded state. I am trying to cache the objects that were expanded so they can be re-expanded after re-binding. The children work as I would expect, however when I try to expand a grandchild of the TreeView I get a null object.
To get a TreeViewItem I use the ItemContainerGenerator property of the ItemsControl:
TreeViewItem cfItem = treeView.ItemContainerGenerator
.ContainerFromItem(obj) as TreeViewItem;
cfItem.IsExpanded = true;
The second level collections all have a reference to their parent object. So since I have many of these object, they are looped over and each uses it's parent object to find the TreeViewItem of it's parent. The order in which they are added to the collection guarantees (I think) that the children will always be processed after the parent. Thus I get this ugly line of code:
qualItem = (
(TreeViewItem)treeView.ItemContainerGenerator
.ContainerFromItem(
((Child)obj).ParentObject
)
)
.ItemContainerGenerator.ContainerFromItem(obj) as TreeViewItem;
This line always fails when it attempts to get the container from item obj. It successfully gets the parent TreeViewItem, but when attempting to get the Child's TreeViewItem container, I always receive null. The documentation states that ContainerFromItem() returns
"A System.Windows.UIElement that
corresponds to the given item. Returns
null if the item does not belong to
the item collection, or if a
System.Windows.UIElement has not been
generated for it."
I know that the second level child exists in the parent's item collection. I checked in the debugger in VS 2010, and all of the items are there. I spent a good bit of time on Google searching for an answer and came across someone who said that the container items are created on a background worker thread and may not be generated by the time an attempt is made to get the child item container. I tried waiting for the Status property of the ItemContainerGenerator to be equal to GeneratorStatus.ContainersGenerated, but I still got the same result. I need to somehow obtain the TreeViewItem container for my second level children so they can be re-expanded just like the first level children.
"A System.Windows.UIElement that corresponds to the given item. Returns null if the item does not belong to the item collection, or if a System.Windows.UIElement has not been generated for it."
Looks like because of Virtualization, the UIElement you look for doesn't exist when you are looking for it. If the collection is not too large, try turning the Virtualization off.
The solution is to add another layer of abstraction between the data and the TreeView. The top level collection is an ObservableCollection, and that contains several ViewModels, which implement INotifyPropertyChanged, and also have ObservableCollections of elements that are in the tree beneath it. This system allows WPF to more easily bind and keep track of the elements that are being added and deleted.
My main source of information was this article:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx