ListView Inside Child TreeViewItem? - wpf

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

Related

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.

How to present a collection of objects in a listbox in WPF

I have a collection of objects that I want to present. How can I do this? A listbox would do but each object has many attributes which I want to present. I already bound a listbox to a collection and I have all my objects listed. My question is regarding the visualization of the listbox and if the listbox is the correct thing to use or there is something else that I should use.
I find the existing answers to be a bit lacking, normally one would not process collections or boil their items down to a string, at the very best you would do some dynamic manipulation using a CollectionView (e.g. sorting, grouping), but normally you use Data Templating to display the individual items which allows you to use all their properties.
Further there are several controls which work well with collections, firstly you need to know if you want selection, if not an ItemsControl is a good choice, otherwise you should use a ListBox or ListView.
ListViews are normally employed if you have different views for your objects, e.g. a details view and a thumbnail view. You can use the ListView.View for this, there is one existing view in the framework, the GridView, which offers columns. What Matthew Ferreira suggested is exactly what you should not do with a ListView since you want to make the templates dependent on the current view, in fact that code does not even compile since DataTemplate can only have one child.
ListViews are supposed to encapsulate the view logic in their view so it can be changed at will. If you decide to use a ItemsControl or ListBox then setting the ItemTemplate is what you want to do. Read the Data Templating overview i linked to, it makes for a good starting point.
You might want to consider using a ListView control instead. ListView has support for columns if you are planning on showing several properties from your object. You can use the ItemTemplate property to format the display of your object. For example:
<ListView ItemsSource="{Binding Path=myObjectCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}"/>
<CheckBox IsChecked="{Binding Path=ShouldCheck}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This example assumes that your object has the properties Title and ShouldCheck.
Your collection of object is probably to be viewed as your model. The usual thing in WPF is to add a ViewModel that translates and exposes the model data into a form suitable for binding. Depending on what you want to do, your VM could e.g. format each object into a string representation and then expose it as a collection of strings that the Listbox can bind to and display.

WPF Treeview with HierarchicaldataTemplate - don't show lowest item

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).

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.

Multiple ListBoxes binding their SelectedItem to the same property in ViewModel - better way?

I have a WPF listview, and in one column the cell may contain one or more ListBoxes.
When I right-click a ListBox I'm building a context menu where each item has a DelegateCommand. Currently I'm setting the command parameter to a SelectedListBox property on the page viewmodel itself as my delegate command needs to know which ListBox has been right-clicked.
However this is leading to weird behaviour, which I'm assuming is because I'm binding multiple ListBoxes to the same page-level property (SelectedListBox).
The relevant XAML for the cell template for the listview is as follows:
<DataTemplate x:Key="MultipleListBoxCellTemplate">
<ListBox SelectedItem="{Binding Path=DataContext.SelectedListBox, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}}" />
</DataTemplate>
Is there a better way to get which ListBox has been right-clicked to my viewmodel, or can anyone think of another approach? Much appreciated :)
When you are building the context menu, you know which list box was selected, yes? I would probably wrap that up in the ICommand you are binding the context item to. This way, each command knows exactly which ListBox it was created by and can get the selected item from there.
Alternately, you may be able to get around the issue with using SelectedItem by changing your binding to OneWayToSource so that the data only flows from the View back to the ViewModel. You may still have timing issues, which I suspect is your current problem, but depending on exactly what is going on, that may resolve it.

Resources