WPF TreeView parent node binding - c#

I have the following classes -
public class A : INotifyPropertyChanged
{
public string Name { get; set; }
public ObservableCollection<Child> Children { get; set; }
...
}
public class Child : INotifyPropertyChanged
{
public string InstanceName { get; set; }
...
}
And here is my TreeView XAML -
<TreeView ItemsSource="{Binding ListOfClassA}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
In this case, the parent class A name is not displayed. How can I show the Name property on class A as well as the InstanceName property on the Child class.

In your code
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
Is actually the template for your A class. It should be:
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<!-- this is how Child is visualised-->
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<!-- this is how A is visualised-->
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
For better code visibility you can write them in resource section as two separate templates:
<HierarchicalDataTemplate x:Key="Child_template">
<!-- this is how Child is visualised-->
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="A_template" ItemsSource="{Binding Path=Children}"
ItemTemplate="{StaticResource Child_template}">
<!-- this is how A is visualised-->
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
Hope this helps.
For reference: http://msdn.microsoft.com/en-us/library/dd759035(v=vs.95).aspx

Related

Create a treeview from a List<customClass> WPF

I would like to create an TreeView from an List<PhonesFromStudents>.
My custom class is PhonesFromStudents
public class PhonesFromStudents
{
public string Name { get; set; }
public List<string> Phones { get; set; }
}
XAML is :
<TreeView x:Name="tv_source" Grid.Row="2" Grid.Column="1" Margin="0,5" HorizontalAlignment="Stretch" ItemsSource="{Binding ListStudents}" Background="White" BorderBrush="{x:Null}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
MainWindows.cs
internal MainWindow(List<PhonesFromStudents> list)
{
InitializeComponent();
this.ListStudents = list;
this.DataContext = this;
}
Example :
2 students
Thomas - iPhone8,iPhone6
Lucas - iPhone4s, S8
i would like to have
Thomas
|_____iPhone8
|_____iPhone6
Lucas
|_____iPhone4s
|_____S8
But I get an empty list from UI.
Can someone help me?
Thanks
Use an HierarchicalDataTemplate:
<TreeView x:Name="tv_source" Grid.Row="2" Grid.Column="1" Margin="0,5"
HorizontalAlignment="Stretch" ItemsSource="{Binding ListStudents}" Background="White" BorderBrush="{x:Null}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:PhonesFromStudents}" ItemsSource="{Binding Phones}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
...and make sure that ListStudents is a public property:
public List<PhonesFromStudents> ListStudents { get; set; }

How do I set a DataTemplate for a WPF TreeView to display all Elements of an List?

I'd like to visualize the following data structure using TreeViews in WPF:
class MyDataContext
{
ICollectionView Outers {get;set;}
//...
}
class Outer
{
string Name {get;set;}
IEnumberable<Inner> Actions {get;set;}
}
class Inner
{
string Description {get;set;}
Command OnClick {get;set;}
}
This is my attempt so far:
<!-- DataContext is MyDataContext at this point -->
<TreeView ItemsSource="{Binding Path=Outers}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type myns:Outer}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}"/>
<TreeView ItemsSource="{Binding Path=Actions}" >
<DataTemplate DataType="{x:Type myns:Inner}">
<Button Command={Binding Path=OnClick}>
<TextBlock Text="{Binding Path=Description}"/>
</Button>
</DataTemplate>
</TreeView>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
It seams like there's something wrong with this access since I get the following InvalidOperationException:
Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
If I drop the inner TreeView there's no exception (but also no buttons of course).
I used the page Mateusz mentioned (HierarchicalDataTemplate) and after reading the answer to this question: Bind Collection to StackPanel I found a solution that did what I wanted:
Here the players (level 3) are on the same row as the team (level 2):
<TreeView ItemsSource="{Binding League}">
<!-- Conference template -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Teams}">
<TextBlock Foreground="Red" Text="{Binding Name}" />
<!-- Team template -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<ItemsControl ItemsSource="{Binding Players}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Please try to use HierarchicalDataTemplate with TreeView.
HierarchicalDataTemplate
Maybe this helps you a little bit ;)
The xaml:
<TreeView ItemsSource="{Binding Outers}">
<TreeView.ItemTemplate>
<DataTemplate>
<TreeViewItem ItemsSource="{Binding Actions}" Header="{Binding Name}">
<TreeViewItem.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Click}" Content="{Binding Name}" />
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The data:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyDataContext();
}
}
class MyDataContext
{
public ObservableCollection<Outer> Outers { get; set; }
public MyDataContext()
{
Outers = new ObservableCollection<Outer>();
Outers.Add(new Outer() { Name = "Herp" });
Outers.Add(new Outer() { Name = "Derp" });
}
}
class Outer
{
public string Name { get; set; }
public ObservableCollection<Inner> Actions { get; set; }
public Outer()
{
Actions = new ObservableCollection<Inner>();
Actions.Add(new Inner { Name = "Test1" });
Actions.Add(new Inner { Name = "Test2" });
Actions.Add(new Inner { Name = "Test3" });
Actions.Add(new Inner { Name = "Test4" });
Actions.Add(new Inner { Name = "Test5" });
Actions.Add(new Inner { Name = "Test6" });
Actions.Add(new Inner { Name = "Test7" });
}
}
class Inner
{
public string Name { get; set; }
public ICommand OnClick { get; set; }
}
And if you are using Commands...
Try it with this example:
ICommand

WPF Treeview Bindings

I'm trying to bind an ObservableCollection to a treeview in WPF. It does kind-of work but not quite the way I thought it would.
This is the binding I have setup
<TreeView Height="250" ItemsSource="{Binding Path=TheUsers}" VerticalAlignment="Bottom" Width="500" Margin="0,39,0,0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Permission}">
<TextBlock Text="{Binding}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
This is the collection it is binding to:
ObservableCollection<UserViewModel> theUsers;
public ObservableCollection<UserViewModel> TheUsers
{
get
{
return theUsers;
}
set
{
theUsers = value;
OnPropertyChanged("TheUsers");
}
}
This is the object in the collection:
public class UserViewModel
{
string userName = null;
public string UserName
{
get
{
return userName;
}
set
{
userName = value;
OnPropertyChanged("UserName");
}
}
int permCount = 0;
public int PermCount
{
get
{
return permCount;
}
set
{
permCount = value;
OnPropertyChanged("PermCount");
}
}
List<string> permission = null;
public List<string> Permission
{
get
{
return permission;
}
set
{
permission = value;
OnPropertyChanged("Permission");
}
}
This is what it displays
What I would like it to do is display the UserName for UserViewModel and the Permissions List<string> items as the children. What is the proper way to do this ?
Thanks in Advance!
Use a HierarchicalDataTemplate like
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:UserViewModel}" ItemsSource="{Binding Permission}" >
<TextBlock Text="{Binding UserName}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
You should be able to do this with a HierarchicalDataTemplate in addition to a normal DataTemplate:
<TreeView Height="250" ItemsSource="{Binding Path=TheUsers}" VerticalAlignment="Bottom" Width="500" Margin="0,39,0,0">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:UserViewModel}" ItemsSource="{Binding Permission}">
<TextBlock Text="{Binding Path=UserName}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type sys:String}" >
<TextBlock Text="{Binding}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
Add the following namespaces to your control/window:
xmlns:local="clr-namespace:WhateverYourAssemblyNamespaceIs"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
With a bit of help from the MSDN DataTemplating overview, I think the following will do what you want. Note that I've added a top-level 'All Users' item to the treeview:
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:UserViewModel}" ItemsSource="{Binding Path=Permission}">
<TextBlock Text="{Binding Path=UserName}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
</Window.Resources>
...
<TreeView Height="250" VerticalAlignment="Bottom" Width="500" Margin="0,39,0,0">
<TreeViewItem ItemsSource="{Binding Path=TheUsers}" Header="All Users" />
</TreeView>
Replace the prefix local with a namespace prefix bound to the namespace your UserViewModel class lives in.
You can set IsExpanded="True" on the <TreeViewItem> if you want the treeview to appear with the All Users item expanded.
Note that I used an ordinary DataTemplate for the permissions, because I don't think you want the treeview to expand any further beyond a permission. (The tree could expand, because permissions are strings, and strings implement IEnumerable<char>. If you tried to expand a string, you'd get a list of the individual characters of the string.)

XAML ListBox only shows class name

How do I get my class properties to show up in the ListBox?
XAML:
<ListBox x:Name="lstPlayers" >
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Player.FirstName}"></TextBlock>
<TextBlock Text="{Binding Player.LastName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox>
C#:
public class Player
{
string FirstName { get; set; }
string LastName { get; set; }
}
public void LoadPlayers()
{
foreach (Player player in Players)
{
lstPlayers.Items.Add(player);
}
}
The only thing that shows up in the ListBox is
TestApplication1.Player
You have some problems with you current implementation. First, the DataTemplate should be placed inside the ItemTemplate for the ListBox. Second, the DataContext for each ListBoxItem will be an instance of Player so you should bind directly to FirstName and LastName. Third, the properties in Player should be made public for the DataBinding to work.
<ListBox x:Name="lstPlayers" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"></TextBlock>
<TextBlock Text="{Binding LastName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class Player
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Also, instead of adding the collection item by item to the ListBox, just set it as ItemsSource
lstPlayers.ItemsSource = Players;
DataTemplate should be inside ListBox.ItemTemplate.
set the collection, Players as ItemSource
and
<ListBox x:Name="lstPlayers" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"></TextBlock>
<TextBlock Text="{Binding LastName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
you have to add the DataType to your DataTemplate.
<DataTemplate DataType="{x:Type local:Player}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"></TextBlock>
<TextBlock Text="{Binding LastName}"></TextBlock>
</StackPanel>
</DataTemplate>
local is the namespace for your TestApplication1.Player. you can set the datatemplate to the listebox.itemtemplate or as a resource of any "parent object"

Can't cast treeviewitem as treeviewitem in wpf

I've got webservice asmx, and there are classes:
Country
public string Name {get;set;}
public string Code {get;set;}
public List<Area> Areas {get;set;}
Area
public string Name {get;set;}
public string Code {get;set;}
public List<Regions> Provinces {get;set;}
Provinces
public string Name {get;set;}
public string Code {get;set;}
I bind it to mz TreeView WPF:
Country[] items = new MyService().GetListOfCountries();
structureTree.ItemsSource = items;
Code of myTree:
<UserControl x:Class="ObjectsAndZonesSimpleTree"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<Grid>
<StackPanel Name="stackPanel1">
<GroupBox Header="Choose" Height="354" Name="groupBox1" Width="Auto">
<TreeView Name="structureTree" SelectedItemChanged="structureTree_SelectedItemChanged" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}" Height="334" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible" Width="Auto" PreviewMouseRightButtonUp="structureTree_PreviewMouseRightButtonUp" FontFamily="Verdana" FontSize="12" BorderThickness="1" MinHeight="0" Padding="1" Cursor="Hand" Margin="-1">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type MyService:Country}"
ItemsSource="{Binding Path=ListOfRegions}">
<StackPanel Orientation="Horizontal">
<TextBlock TextAlignment="Justify" VerticalAlignment="Center" Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type MyService:Region}"
ItemsSource="{Binding Path=Provinces}">
<StackPanel Orientation="Horizontal">
<TextBlock TextAlignment="Justify" VerticalAlignment="Center" Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type MyService:Province}"
ItemsSource="{Binding Path=ListOfCities}">
<StackPanel Orientation="Horizontal">
<TextBlock TextAlignment="Justify" VerticalAlignment="Center" Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</GroupBox>
</StackPanel>
</Grid>
</UserControl>
This gives me null:
private void structureTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeViewItem treeViewItem = structureTree.SelectedItem as TreeViewItem;
}
SelectedItem will actually contain a Country, Area, or Region (or null). If you really want the TreeViewItem, you can do strutureTree.ItemContainerGenerator.ContainerFromItem(structureTree.SelectedItem).
Correct. You should expect a Country as your SelectedItem. WPF works entirely different than Windows Forms did. It's all about databinding!

Categories