WPF - I'm stumped on the TreeView control - c#

Pun intended.
I want to create a simple TreeView using the HierarchicalDataTemplate class.
Here's my problem XAML:
<Window.Resources>
<ObjectDataProvider
x:Key="myDataProvider"
ObjectType="vm:ContractViewModel" />
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource myDataProvider}" Path="Contract" />
</Window.DataContext>
<StackPanel
Orientation="Vertical"
VerticalAlignment="Top">
<ListBox MinWidth="400" Margin="10"
ItemsSource="{Binding Commissions}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Id}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TreeView>
<HierarchicalDataTemplate DataType="{x:Type m:Contract}"
ItemsSource="{Binding Commissions}">
<TextBlock Text="{Binding Id}" />
</HierarchicalDataTemplate>
</TreeView>
</StackPanel>
I'm using the MVVM pattern. The StaticResource "myDataProvider" returns an instance of a Contract (custom) class. Here's my model:
internal class Contract
{
public string Name { get; set; }
public ObservableCollection<Commission> Commissions { get; set; }
}
internal class Commission
{
public string Id { get; set; }
}
FYI - my model is actually more complex; my classes contain more members than shown, they have constructors, and they implement INotifyPropertyChanged.
In my test, I load two Commission objects into a Contract object. The listbox works as expected: I can see the Id of each Commission object w/in Contract. The TreeView doesn't work: it returns a "System.Windows.HierarchicalDataTemplate" string in the TreeView control where I'd expect each Commission Id to be listed.
I've referred to other posts and MSDN to no avail. I'd be appreciative of your help!

From what I can tell is that you're not using the TreeView correctly in XAML. You need to put your HierarchicalDataTemplate at the TreeView.Resources level and assign a ItemsSource
As shown here you want to set the Template of the item.
Try to do something like this instead:
<TreeView ItemsSource="{Binding Contracts}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type m:Contract}"
ItemsSource="{Binding Commissions}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type m:Commission}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Id}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I personally do it like this--using RadTreeView:
<telerik:RadTreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type vm:BaseType}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Title}" />
</HierarchicalDataTemplate>
</telerik:RadTreeView.ItemTemplate>

Related

WPF binding user-control in DataTemplate

I am struggling to bind my user-defined tooltip to the items of a TreeView.
This is the XAML inside the TreeView: as you can see I bind my HierarchicalDataTemplate to the GroupWrapper (simple Class that exposes the properties Name and Children) and the DataTemplate to the DocWrapper (again, simple Class that has properties such as Name, Icon, BsonContent).
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type tmistruct:GroupWrapper}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type tmistruct:DocWrapper}" >
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" Tag="{Binding BsonContent}" VerticalAlignment="Center" Style="{StaticResource TreeViewTextboxItemStyle}"/>
<StackPanel.ToolTip>
<local:MyDocTooltip
NameField="{Binding Name}"/>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
It all works great! The Tree is automatically populated with the correct text and icons for all nodes BUT... my user-defined tooltip doesn't get the memo. The tooltip shows, but the name field is not populated. Note that if I replace
<local:MyDocTooltip
NameField="{Binding Name}"/>
with:
<local:MyDocTooltip
NameField="TESTSTRING"/>
The test string is correctly displayed in the Tooltip.
This is what my user-defined tooltip looks like:
namespace MyControls
{
/// <summary>
/// Interaction logic for MyDocTooltip.xaml
/// </summary>
public partial class MyDocTooltip : UserControl
{
public MyDocTooltip()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty NameFieldProperty = DependencyProperty.Register("NameField", typeof(string), typeof(MyDocTooltip));
public string NameField
{
get { return (string)GetValue(NameFieldProperty); }
set { SetValue(NameFieldProperty, value); }
}
}
}
I guess something to do with my tooltip's data context being set to its own ViewModel? I've been trying with relative references but with no luck. Any help appreciated! Cheers.
OK my whole setup was wrong. (Thanks #ASh)
For those out there who are struggling with similar issues:
Each user control should not set its DataContext property!
This should be inherited.
What you do inside each user control, is to bind each element to its UserControl ancestor.
So, inside MyDocTooltip.xaml each element will be defined like this (bound to NameField property):
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=NameField}"/>
Then I do the same in the TreeView (which is also inside a user control):
<TreeView x:Name="treeViewCollection" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=FilteredGroups, UpdateSourceTrigger=PropertyChanged}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type tmistruct:GroupWrapper}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate x:Name="TreeViewDataTemplate" DataType="{x:Type tmistruct:DocWrapper}" >
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" Tag="{Binding BsonContent}" VerticalAlignment="Center"/>
<StackPanel.ToolTip>
<local:MyDocTooltip
NameField="{Binding Name}"
/>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
And it all seems to work fine. Please do let me know if I'm still doing it wrong. Thanks for the help.

WPF Treeview with different collections for each depth

i am completly new to WPF and need your help. I followed many tutorials but nothing works.
I have two ObserveableLists L1 and L2 to bind and I want to archiev:
On depth 1 - The first List and for each child list 2.
L1.1
L2.1
L2.2
L2.3
L1.2
L2.1
L2.2
L2.3
L1.3
L2.1
L2.2
L2.3
Update 16.02.07 - 16:44
My first try:
<TreeView Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Margin="10" ItemsSource="{Binding orderCities}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type model:city}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding products}" DataType="{x:Type model:Product}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My second try: Defining two DataTemplates how to handle my types:
<UserControl.Resources>
<HierarchicalDataTemplate DataType="{ x:TypeExtension model:city }" ItemsSource="{Binding orderCities}">
<StackPanel>
<TextBlock Text="{Binding name}" />
<!-- Here embed Product Type (Dont know how)-->
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{ x:TypeExtension model:Product }" ItemsSource="{Binding products}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
<TextBlock Text=" - " />
<TextBox Text="1" />
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
And testet both templates and got the right design.
<TreeView ItemsSource="{Binding orderCities}" />
<TreeView ItemsSource="{Binding orderCities}" />
The treeview alone won't help you here. You have to actually create the data you want to display, i.e. the treeview won't "multiply" the two lists.
Each element in the first list may of course return the same instance of the second list in its children-property. You might want to have look at this codeproject article, it's rather old, but gives a nice introduction on how to use the wpf treeview.

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 can I create a Hierarchical data template using tree view?

I'm using a class created from entity frameworks (Categories table in database)
And only contains three fields:
CategoryID
CategoryName
ParentCategory
And entity framework created me two navigation: Subcategories and Parent
And when I load the collection in a treeview, it show me everything, where it should only show the top levels.
I think I should create a hierarquical data template, but I really have no idea about creating it.
EDIT: It similars these case: Entity Framework - Binding WPF Tree view control
My control XAML contains:
<TreeView x:Name="objectiveTree" ItemsSource="{Binding Objectives}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Subcategories}">
<TextBlock Text="{Binding Path=CategoryName}"
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
You can try something like this:
Code-behind:
objectiveTree.ItemsSource = (List<YourMainEntity>) _entities;
XAML:
<TreeView x:Name="objectiveTree">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Subcategories}">
<TextBlock Text="{Binding ParentCategory}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryName}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<TreeView ItemsSource="{Binding YourItems}" ItemContainerStyle="
{StaticResource Level1}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}"
ItemContainerStyle="{StaticResource Level3}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Nested databinding with WPF and C#

I'm trying to make a budget program. Where I need to have groupboxes with a list of textblocks inside.
<ItemsControl DataContext="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<GroupBox Header="{Binding}">
<ItemsControl DataContext="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I need somehow to databind a list (perhaps?) with groupboxes so I'd create a list of group boxes, with some lines inside that would be a text with a currency value. So that I could create a group called "Apartment", with two lines "Rent $3000" and "Maintenance $150". Then I could have a second group called "Car" with lines "Insurance", "Loan" and "Maintenance" for instance.
But how would I databind this? And how would I need in C# to perform this. I'm at a loss.
Building off of Jay's comment, you would want to create a Hierarchical data model. Note I have left implementing INotifyPropertyChanged on the properties to you
public class BudgetLineItem : INotifyPropertyChanged
{
public string Name { get; set; }
public decimal Cost { get; set; }
}
public class BudgetGroup : INotifyPropertyChanged
{
public string GroupName { get; set; }
public ObservableCollection<BudgetLineItem> LineItems { get; set; }
}
public class BudgetViewModel : INotifyPropertyChanged
{
public ObservableCollection<BudgetGroup> BudgetGroups { get; set; }
}
Then your data-template would look like this:
<ItemsControl DataContext="{Binding ViewModel}"
ItemsSource="{Binding BudgetGroups}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<GroupBox Header="{Binding GroupName}">
<ItemsControl ItemsSource="{Binding LineItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Cost}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I could be off base here, but it sounds like you want to change the DataTemplate based on the type of object that is being bound from a list of heterogeneous objects.
If that's the case, you want to look into DataTemplateSelectors or create DataTemplates for each of the types you want to support in the list.
For example, for an Apartment you might have:
<DataTemplate DataType="local:ApartmentBudget">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
a Car may look like:
<DataTemplate DataType="local:CarBudget">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Insurance}" />
<TextBlock Text="{Binding Loan}" />
<TextBlock Text="{Binding Maintenance}" />
</StackPanel>
</DataTemplate>
Then your ItemsControl can be set like:
<ItemsControl ItemSource="{Binding BudgetItems}">
The correct DataTemplate will be picked based on the data type. You can have even more control by creating a custom DataTemplateSelector.
See https://msdn.microsoft.com/en-us/library/ms742521(v=vs.100).aspx for more information.

Categories

Resources