WPF Treeview with HierarchicaldataTemplate - don't show lowest item - wpf

I have a WPF TreeView with a HierarchicalDataTemplate. As I descend the hierarchy, expanding the nodes, I will eventually get to the bottom and data displayed via a normal DataTemplate.
I'd like not to show those nodes - if I set the DataTemplate's containing TextBlock to Visible Hidden (or similar) I just get allocated space in the treeview. I'd like not to display those items so assume I need to remove them somehow. I cannot use a Filter on a CollectionView as there may be other nodes with children at this level. So basically, at any level I want to remove those nodes that have no children. The actual data is being loaded from an Xml file via an XmlDataProvider, so there are no class objects.
Can anyone suggest how
thanks
John

Presumably you ask the question because the last nodes in the tree are of the same type so you use only the one HierarchicalDataTemplate:
<HierarchicalDataTemplate DataType="{x:Type src:MyNodeClass}" ItemsSource = "{Binding Path=Items}">
<TextBlock Text="{Binding Path=PropertyToDisplay}"/>
</HierarchicalDataTemplate>
If you could change the type of your last nodes they will not automatically use the template (you could also inherit from the normal type so the collection allows them even though the new class is actually empty).

Related

WPF: How to bind to only one item in a collection, not using ItemsControl since I don't want to display all of them

I have this requirement, that I have a collection of items (ObservableCollection), but I only want to display the first item. The requirement comes from the fact that in most of the case, the collection only contains one item. And due to the space limit, even if there is more than one items in the collection, we'd like to display the number of the items, details of the first one (same presentation as prior situation) and a ... symbol to indicate to the user that there is more items. And when the mouse is over the UI element a popup will eventually display all items.
The first solution I can think of (please suggest others if they are better) is to bind to this collection (but not using an ItemsControl) and define a DataTemplateSelector derived class (which return either the DataTemplate to display the only one item, or the DateTemplate which has the ... and the popup for more details, based on the number of items in the collection) and use it as ContentTemplateSelector.
But now my question: how both of my DataTemplate would look like in XAML, so that they can display only the first item in the collection? Obviously I can't have a ItemsControl.
UPDATE:
Now I have managed to make it work and agree this question can be closed (I can't delete it anymore since there is already some answers).
I actually knew how to bind to one certain item in the collection, but this was not where I am confused. I felt I should use ContentControl as one answer suggests. But I thought since I need to bind to the whole collection (not to single indexed item), and use a DataTemplateSelector to select the proper DataTemplate based on the number of items in the collection. The code would look like this:
<ContentControl Content="{Binding MyCollection}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
And in MyTemplateSelector I wasn't sure how to use it since there is no reference to my collection because it is defined as resource and it doesn't have the information of MyCollection. However, it turned out to be very simple, the DataTemplate can refer to an indexed item without knowing the name or any other reference. Simply like this:
<DataTemplate>
<TextBlock Text="{Binding [0].PropertyName}" />
<DataTemplate />
To bind to just one item from a collection, you can use the following syntax:
{Binding Items[0]}
Or to bind to a property of a single item from the collection:
{Binding Items[0].Property}
You can find out more about property path syntax from the Binding.Path Property page at MSDN... from the linked page:
• Indexers of a property can be specified within square brackets following the property name where the indexer is applied. For instance, the clause Path=ShoppingCart[0] sets the binding to the index that corresponds to how your property's internal indexing handles the literal string "0". Multiple indexers are also supported.
Try this
<ContentControl Content="{Binding YourCollection[0]}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Ok, late to the party but I thought I'd share my 2 cents anyway: I'd better go with a dumber (XAML-)view and a view-model closer to your presentation needs.
Translated: instead of mapping your existing view-model (or raw data) and its collection of items directly to the view, I suggest to map that to an appropriate view-model showing something like a YourItemViewModel FirstItem property and a bool HasMore property. That second view-model would be easily unit-testable to make sure it behaves propertly, and would be easily mapped to a view with less logic, so to avoid possible hard-to-test problems in view.
{Binding Items[0].SomeProperty}
{Binding [0].SomeProperty}
{Path=/SomeProperty}

WPF TreeView question

Is it possible to store some data in every item of a TreeView control? I mean, something useful (e.g. a string) besides the header text?
Thanks.
Yes, WPF is "lookless", so your actual data can be anything you want it to be, and a TreeView is just a Template used to display the data to the user in a pre-determined way.
You can overwrite any part of that Template to be whatever you want, and/or have it bind to your data however you want.
Edit
I'm no expert on using the TreeView, but if you had a DataContext of List<Folder>, and each Folder object had a Name and a FullPath property, your TreeView could look something like this:
<TreeView ItemsSource="{Binding MyFolderList}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
ToolTip="{Binding FullPath}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
If you haven't already, I'd highly recommend looking into the MVVM design pattern when working with WPF. Basically your application is your classes (ViewModels), and the Controls/XAML (Views) are just a pretty layer that sits on top of your classes to make them user-friendly.
This is an important concept when switching from a WinForms TreeView to a WPF TreeView
It depends on what you mean by store data...
If you're just talking UI customization Rachel's answer above works.
If you're talking about storing arbitrary object values, such as information about the TreeViewItem, or maybe a relation between two items, you can use the Tag property of TreeViewItem. For example, I had to write a mapping UI where two trees linked together where each TreeViewItem from the first tree, could connect to 1 TreeViewItems of the second tree. I used the Tag property of the first TreeViewItem to store the connecting TreeViewItem.

ListView Inside Child TreeViewItem?

Hey guys,
I have a WPF TreeView that has three nodes, I would like the last child (the third node) to contain a ListView populated with my bound data. The issue that I'm running into is that if I put a ListView in the ItemTemplate of my HierarchicalDataTemplate I get a ListView for each child rather than a single ListView with content. This is expected behavior per the documentation, but I'm looking for a work around (possibly modify the ItemContainerStyle), unfortunately I have virtually no experience with the TreeViewItem's control template, or for that matter much experience with TreeViews in general.
I've looked at the posibility of using a TreeListView but it doesn't fit my use case, nor does binding a separate control and displaying data that way.
Has anyone either already gone through the headache of sandwiching a ListView into a TreeViewItem, or can someone suggest how to modify the ControlTemplate to accomplish this?
Thank you,
Aj
The node that has the Listview should be a different data type than the other two nodes so it can be handled differently by the Treeview.
Also it sounds like the child that contains the list view data should not be in a HierarchicalDataTemplate but a regular DataTemplate one, since it doesn't have other child elements recognized by the Treeview. You can mix and match HierarchicalDataTemplate and regular DataTemplates in a Treeview as you see fit, regular DataTemplates represent "leaf" nodes, they don't have any children.
sample with different data types used in Treeview but still using hierarchical templates:
<HierarchicalDataTemplate DataType="{x:Type FooNormal}" ItemsSource="{Binding TreeviewChildNodeCollection}">
// do something else here.
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type FooList}" ItemsSource="{Binding TreeviewChildNodeCollection}">
<ListView ItemsSource="{Binding ListviewChildNodeCollection}">
//use listview here
</ListView>
</HierarchicalDataTemplate>
Turns out the easiest way to deal with the situation is to replace the HierarchicalDataTemplate on the second node with an Expander, set the ItemsSource on the ListView rather than the HierarchicalDataTemplate, and restyle the button on the Expander to look like the one from a TreeViewItem....sorta a hack but it works :)
Thanks for all the help,
Aj

Creating instances of resources?

I'm brand spanking new to WPF and am trying to play around with projects to better understand what I'm reading.
My understanding of a resource is that it is the instance, you can't use it like a factory and create instances of it. For example, a XAML-defined rectangle. You can reference it, but you can't have numerous instances of it all over the surface.
In WPF, what would be the way to do that? If I define a Rectangle as a resource with specific properties and wanted to have multiple instances of that within a dynamically-generated grid, how should I be going about it? Or is there a different way I should be trying to do this?
Purely academic exercise with no real-world application.
Actually there's nothing about resources in particular that prevents you from using it multiple times. A perfect example of this is brush resources, style resources, etc. You define them in XAML and the XAML parser creates a single instance of the resources and stores them in the resource dictionary and these brushes, styles, etc can be used as property values many times even though only a single instance of the resource was created.
But having said that, as you noted, you can't really define a Rectangle resource and use it multiple times in the visual tree. This has nothing to do with the fact that it's a resource, but rather it has to do with the fact that a FrameworkElement cannot be a child of more than one parent element.
So what we have instead is called "templates". These tell WPF how to create an element tree but does not actually create the tree until you instantiate the template. Below is an example.
<UserControl>
<ItemsControl ItemsSource="{Binding WholeBunchOfItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill="Yellow" />
<ContentPresenter Content="{Binding}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
In this example I've bound an ItemsControl to a collection of some sort. For each item in the collection, the ItemsControl will use my DataTemplate to render the item. Within a DataTemplate you can use data binding to access the current item.
I would suggest reading up on MSDN about ControlTemplate, DataTemplate, and Style. These are all important concepts in WPF/Silverlight.
To get multiple instances replicated across a grid or listbox, you need to set the data template to define the UI controls for each row of data, and then databind the grid or listbox to a collection of data that determines how many rows and the individual field values.
Key term for you to research first: data template.

Silverlight 3 - Find Element inside items control

I have an ItemsControl that is bound to a collection of objects. Each object has it's own collection as well as other vital properties. To display the objects within an object, I'm displaying a TreeView inside of a ItemsControl. I know this sounds crazy. But, this is just a trimmed down version of what I am trying to accomplish to keep the question focused on the problem. Here is my sample:
<ItemsControl x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:TreeView x:Name="myTreeView">
</controls:TreeView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When a user clicks a button, I need to retrieve the current TreeView associated with a specific object. In an attempt to do this, I am trying the following:
MyClass instanceToFind = (MyClass)(IdentifyDesiredInstance());
foreach (MyClass instance in myItemsControl.Items)
{
if (instance.ID == instanceToFind.ID)
{
TreeView treeView = null; // How do I get the TreeView?
// Do other necessary updates
}
}
The code snippet above shows where I am trying to get the TreeView. How do I get the TreeView when looping through the items in an itemscontrol?
Thank you!
You need to use the VisualTreeHelper.GetChild and VisualTreeHelper.GetChildrenCount methods to iterate through the view's children until you find the tree that corresponds to your item. You should be able to check the TreeView.DataContext property against your item to verify its the right one. Note, you need to use this recursively as GetChild only retrieves immediate children.
As you'd need to iterate the visual tree anyway, I recommend abandoning your current loop and instead, just looping the children, checking the ID of their data contexts.

Resources