Populating a WPF treeview with a list with nested lists - c#

I got the following setup in my application;
5 datatypes;
Area
System
Equipment
Device
Signal
Now, Area is always the highest level, but can contain chidren of all the other four types - so I've added a list of Areas, and under each area there will be a nested list of the other types.
A system will always be a children of an Area, but can contain children of the last three types - and so on.
See my nested layout here
Now I want to populate a treeview with this nested layout. I've already made a list of areas with nested lists with the other types - but how do I get a treeview to show this hierarchy?

You are looking for HierarchicalDataTemplate and DataTemplate.
Because your TreeView is supposed to support multiple different Templates you should place them is the resources of the TreeView.
<TreeView ItemsSource="{Binding Path=CollectionWithRootElement}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type blabla:Area}" ItemsSource="{Binding Path=SubItemsCollectionOfThisType}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Area Type" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type blabla:System}" ItemsSource="{Binding Path=SubItemsCollectionOfThisType}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="System Type" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type blabla:Signal}" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="Signal Type" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
A few things to note:
The TreeView need a Collection to bind to, if there is only one Area you might need to create a collection with only one Area element.
HierarchicalDataTemplate has children, DataTemplate does not. Any class with nested collections is therefore represented by an HierarchicalDataTemplate. The 'Leaf' elements which have no further children are represented by DataTemplate.
The XAML above uses the dummy namespace blabla. You have to adjust it to fit you needs e.g. <Window xmlns:blabla="clr-namespace:MyApp.Namespace"></Window>

Related

Adding custom items to stackpanel

I'm working on a project (for Windows Phone 8 with Visual Studio 2012 with C#) where I want to display some items that each have:
a picture
a title
a description
to be able to be clicked (so that I can navigate to a certain Page)
So I thought I could do that with a stackpanel. But I'm not sure how I can add items that have the above properties and to be able to add those items from XAML. I tired adding items through a ItemsControl in stackpanel but I'm not sure how I can add more complex items like the one I want.
The best approach is to use a ListBox or LongListSelector rather than a StackPanel. You can then:
Data bind the list to the control itself, which will handle adding/deleting items from the control automatically
Define the view for each control using ListBox's ItemTemplate property
First of all, in your code-behind/ViewModel/what-have-you, you'll want to create an ObservableCollection of objects to display. ObservableCollection will let the control know to update in the case an item is added, removed, etc.
public ObservableCollection<T> foo = new ObservableCollection<T>();
In XAML, you'll then want to databind this ObservableCollection to the ListBox you've created:
<ListBox x:Name="ListBox" ItemsSource="{Binding foo}" />
Finally, you can define the ItemTemplate of the ListBox like so:
<ListBox x:Name="ListBox" ItemsSource="{Binding foo}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Description}" />
<Image Source="{Binding Image}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd highly recommend reading this guide, especially "Binding a control to a collection of objects" and the section after on DataTemplates. :)

How to create a TreeView with child nodes of different UI Controls

In my program I have a treeView for which I want to allow child nodes that are comboBoxes and textBlocks. My treeView along with it's nodes are created in an MVVM style. I know how to create textBlock child nodes, but have never created child nodes of any other UI tool.
My treeview's xaml:
TreeView ItemsSource="{Binding UserControl_DataModel.TreeViewViewModel.ObservableCollection<TreeViewDataModel>}" DisplayMemberPath="DisplayName.Value".../>
This is where new nodes are created and added to the TreeView (TreeViewViewModel):
private TreeViewDataModel createNewNode(string nodeName)
{
var newNode = new TreeViewDataModel ()
{
DisplayName = nodeName
};
newNode.Children.Add(new TreeViewDataModel () { DisplayName = nodeName});
return newNode;
}
public void addNewLocNode(string nodeName)
{
TreeObservableCollection.Add(createNewNode(nodeName));
}
How do I create child nodes that are ComboBoxes, while still allowing the textblock children? This confuses me, because I don't see any part of the code that specifies what UI tool the children become. Please let me know if you need to see more code.
(Previous question about this same treeView if you need to reference it.)
Update:
Just for clarity, this is how my treeView nodes should look:
When I try something like this:
<HierarchicalDataTemplate DataType="{x:Type Data:Node}" ItemsSource="{Binding Teams}">
<StackPanel>
<TextBlock Text="{Binding IndividualProperty}" />
<ComboBox ItemsSource="{Binding CollectionProperty}" />
</StackPanel>
</HierarchicalDataTemplate>
I instead get this setup (it's like each item of the tree comes with a TextBlock and a ComboBox. Maybe there is another option besides StackPanel?):
Note that the comboBox is not a child of the textBlock node, but it's more like it's on the same level as the textBlock node.
A ComboBox is a collection control. Therefore, you need a collection property in your 'node' object to bind to the ComboBox.ItemsSource. While an individual property in the 'node' object might need a HierarchicalDataTemplate somewhat like this:
<HierarchicalDataTemplate DataType="{x:Type Data:Node}" ItemsSource="{Binding Teams}">
<TextBlock Text="{Binding IndividualProperty}" />
</HierarchicalDataTemplate>
You would need one more like this:
<HierarchicalDataTemplate DataType="{x:Type Data:Node}" ItemsSource="{Binding Teams}">
<ComboBox ItemsSource="{Binding CollectionProperty}" />
</HierarchicalDataTemplate>
Or perhaps like this:
<HierarchicalDataTemplate DataType="{x:Type Data:Node}" ItemsSource="{Binding Teams}">
<StackPanel>
<TextBlock Text="{Binding IndividualProperty}" />
<ComboBox ItemsSource="{Binding CollectionProperty}" />
</StackPanel>
</HierarchicalDataTemplate>

Treeview not showing my children

I've not used the TreeView before other than in a few tutorials to get the hang of them. I thought I had, it turns out I haven't.
I'm trying to bind my TreeView to an object.
The Object is
public List<MyGrandparents> MyGrandParents {get;set;}
Within the MyGrandParent class, there is a property
public List<MyParents> MyParents{get;set;}
and lastly, within the MyParent class there is a property
public List<MyChildren> MyChildren {get;set;}
Within each class, there are other properties such as Name (no base classes though so no shared code at this stage)
I want to bind the lot to a tree view, so at 'root' level I only see grandparents. I can expand grandparents to only see parents, whom I can also expand to see children.
The issue I have is only the highest level is binding (grandparent level). My XAML is
<TreeView ItemsSource="{Binding MyGrandParents}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type local:MyGrandParent}">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" />
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MyParent}" ItemsSource="{Binding MyGrandParents}">
<TextBlock Text="Don't bind to test"></TextBlock>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I am lost as to why this isn't binding to give me a nice Tree.
You are not following correct pattern to use HierarchicalDataTemplate.
Item which might contain children should be declare as HierarchicalDataTemplate with ItemsSource set to child collection you want to show beneath it.
And item not containing any child should be declare as DataTemplate instead.
It should be like this -
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MyGrandParent}"
ItemsSource="{Binding MyParents}">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MyParent}"
ItemsSource="{Binding MyChildren}">
<TextBlock Text="Don't bind to test"></TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:MyChildren}">
<TextBlock Text="Don't bind to test"></TextBlock>
</DataTemplate>
</TreeView.Resources>

How to align the columns of an ItemsControl control (WPF)

I have defined a ItemsControl control in xaml as follows:
<ItemsControl BorderThickness="0" Name="SummaryList">
</ItemsControl>
In the same xaml file, I've defined the following DateTemplate for each item in the ItemsControl:
<DataTemplate DataType="{x:Type app:UpgradeItem}">
<StackPanel Orientation="Horizontal">
<Label Margin="5,5" Content="{Binding Path=ItemText,Mode=OneTime}" />
<Label Margin="5,5" Content="{Binding Path=FromVersion,Mode=OneTime}" />
<Label Margin="5,5" Content="{Binding Path=ToVersion,Mode=OneTime}" />
</StackPanel>
</DataTemplate>
I have bound the ItemsControl's ItemSource to a generlic list of UpgradeItems (i.e. List) and programmatically add to this list during execution. The items in the ItemsControl control populate correctly except that the data in the columns are not aligned. How can I modify this so that the three separate columns (ItemText, FromVersion and ToVersion) are aligned?
TIA
You should be able to use a SharedSize groups on grids, however I feel there is a better way to do this. Here's a link on the subject: http://blogs.microsoft.co.il/blogs/guy/archive/2009/06/30/wpf-grid-shared-size-groups.aspx
Part of what makes this tricky is that the ItemsControl doesn't have "columns": the control itself has no idea how its items' contents are laid out in relation to each other.
Your best bet would probably be to use one of the subclasses of ItemsControl that does support columnar data, such as ListView/GridView or the full-fledged DataGrid.

How to use the wpf treeview with nested objects of different types

I have two classes that I'd like to show in a treeview.
In my main window, I have an observable collection of a certain type of object, call it object a.
Object a contains an observable collection of another type of object, object b.
Each object b contains an observable collection of object b's.
For example, I might have something that looks like this
ListOfObjectA's
-ObjectA1
--ListOfObjectB's
---ObjectB
----ListOfObjectB's
-----ObjectB
-ObjectA2
--ListOfObjectB's
---ObjectB
What should my xaml look like to bind the treeview to something like that?
Sorry if I'm not explaining this well, I'm new to wpf.
RB has the right idea. This is where WPF's ability to apply templates to normal .NET objects becomes very powerful.
You need to specify two HierarchicalDataTemplates, one for objects of type a, and the other for objects of type b. Something like this :
<TreeView>
<TreeView.Resources>
<HierarchicalDataTemplate TargetType="{x:Type local:a}">
<!-- XAML to display objects of type a -->
</HierarchialDataTemplate>
<HierarchicalDataTemplate TargetType="{x:Type local:b}">
<!-- XAML to display objects of type b -->
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
You will need to specify bindings for the ItemsSource property of each template so that WPF knows where to get child objects from. Also, you'll need to add an xmlns declaration to the root node specifying the namespace that the objects live in (represented by local in the sample code above).
I know enough WPF to be really dangerous, but I'm pretty sure the HierarchicalDataTemplate is the solution to your problem. My XAML skill are iffy, so I am can't write good example code for you. Here is how I used the HierarchicalDataTemplate in my project. I hope this gives you some good ideas.
<TreeView Grid.Row="1"
Grid.Column="1"
ItemsSource="{Binding Children}"
SelectedItemChanged="TreeView_SelectedItemChanged"
ContextMenu="{StaticResource MenuContextMenu}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" >
<!--<TextBlock Text="{Binding Path=ItemName}" Margin="5,0,5,0" />-->
<Grid ContextMenu="{StaticResource ContextMenu}" >
<TextBlock Name="ShownItem"
Text="{Binding Path=ItemName}"
Margin="0,0,0,0" />
<TextBox Name="EditBox"
Text="{Binding Path=ItemName}"
Visibility="{Binding Path=Visibility}"
Style="{StaticResource EditableTextBox}"
IsVisibleChanged="EditBox_IsVisibleChanged"
PreviewKeyDown="EditBox_KeyDown"
Margin="0,2,0,2"
>
</TextBox>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Categories

Resources