TabControl: SelectedContent and SelectedItem return the same - wpf

I'm displaying a list of custom objects (here: Customer) dynamicly, each in its own tab using TabControl, ItemsSource and DataTemplate:
MainWindows.xaml.cs:
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace testTabControl
{
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int NumberOfContracts { get; set; }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//create all
var customers = new List<Customer>
{
new Customer {FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23},
new Customer {FirstName = "Jane", LastName = "Smiths", NumberOfContracts = 42},
new Customer {FirstName = "John", LastName = "Tester", NumberOfContracts = 32}
};
//show
myTabControl.ItemsSource = customers;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
Trace.WriteLine("myTabControl.SelectedContent is " + myTabControl.SelectedContent.GetType());
Trace.WriteLine("myTabControl.SelectedItem is " + myTabControl.SelectedItem.GetType());
// do something with content of the selected tab:
///myTabControl.SelectedContent.Foreground = new SolidColorBrush(Color.FromRgb(255, 0, 0));
}
}
}
MainWindow.xaml:
<Window x:Class="testTabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testTabControl="clr-namespace:testTabControl"
Title="MainWindow" Height="350" Width="525"
SizeChanged="OnSizeChanged"
>
<Window.Resources>
<DataTemplate x:Key="myContent" DataType="{x:Type testTabControl:Customer}">
<TextBlock x:Name="myContentRoot">
This is the content for
<TextBlock Text="{Binding FirstName}"/> <TextBlock Text="{Binding LastName}"/>
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="myHeader" DataType="{x:Type testTabControl:Customer}">
<TextBlock Text="{Binding LastName}"/>
</DataTemplate>
</Window.Resources>
<TabControl
x:Name="myTabControl"
SelectedIndex="0"
ItemTemplate="{StaticResource ResourceKey=myHeader}"
ContentTemplate="{StaticResource ResourceKey=myContent}"
/>
</Window>
In the method OnSizeChanged I expected myTabControl.SelectedContent to return a TextBlock generated from DataTemplate. But an instance of Customer is returned! Same as by myTabControl.SelectedItem.
The only way I found to get the generated content is described here: http://msdn.microsoft.com/en-us/library/bb613579.aspx
Does anybody know any other solution without visual tree walk?

Your TabControl contains objects of type Customer. Templates can be used to tell WPF how to draw each Customer object, however it doesn't change the fact that the items in the TabControl are still Customer objects.
If you want to access a templated UI object for the item, you can either walk the VisualTree or use the ItemContainerGenerator to get the container holding the SelectedItem
For example,
var selectedItem = myTabControl.SelectedItem;
var selectedItemContainer =
myTabControl.ItemContainerGenerator.ContainerFromItem(selectedItem);

The reason your SelectedContent is returning a Customer object is this:
<DataTemplate x:Key="myContent" DataType="{x:Type testTabControl:Customer}">
It is returning exactly what you ask for. All the DataTemplate does is describe how to display the Customer object which is the content, nothing else. When you ask for content, it returns the object that the DataTemplate is displaying.
One way that you could do this house a property on your Customer object which replicates the output you want.
public string OutputString
{
get
{
return string.Format("This is the content for {0} {1}", this.FirstName, this.LastName);
}
}
And then do something like
Trace.WriteLine("myTabControl.SelectedContent is " + myTabControl.SelectedContent.OutputString);
Or you might could create a collection of strings that followed the format above, and make those the content. Then your SelectedContent would be a simple string by default.

Related

Pass a ObservableCollection<object> to UserControl

I have been trying to generalize the solution for passing ObservableCollection to a UserControl provided here:
How to bind collection dependency property in UserControl
I changed the code behind UserControl to:
/// <summary>
/// Interaction logic for myUserControl1.xaml
/// </summary>
public partial class myUserControl1 : UserControl
{
#region Public Section
public ObservableCollection<object> UCItems
{
get;
set;
}
#endregion
public myUserControl1()
{
InitializeComponent();
UCItems = new ObservableCollection<object>();
}
#region UCItemsSource Property
public static readonly DependencyProperty UCItemsSourceProperty =
DependencyProperty.Register("UCItemsSource", typeof(IEnumerable), typeof(myUserControl1));
public IEnumerable UCItemsSource
{
get { return (IEnumerable)GetValue(UCItemsSourceProperty); }
set { SetValue(UCItemsSourceProperty, value); }
}
#endregion
}
and changed TexBox to DataGrid in xaml:
<UserControl x:Class="OCasDPdemo.myUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OCasDPdemo"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ItemsControl ItemsSource="{Binding Path=UCItemsSource,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataGrid x:Name="myDataGrid" ItemsSource="{Binding Path=UCItemsSource.Person}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I populate the collection in a similar way to the original example:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Person> WindowCollection
{
get;
set;
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
var bob = new Person { FirstName = "Bob", LastName = "Brown", Age = 32.1, ID = 101 };
var jim = new Person { FirstName = "Jim", LastName = "Green", Age = 21.0, ID = 201 };
var mel = new Person { FirstName = "Mel", LastName = "Black", Age = 20, ID = 111 };
WindowCollection = new ObservableCollection<Person>() {bob, jim, mel };
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var sue = new Person { FirstName = "Sue", LastName = "White", Age = 64.7, ID = 101 };
var ted = new Person { FirstName = "Ted", LastName = "Grey", Age = 18.3, ID = 191 };
WindowCollection.Add(sue);
WindowCollection.Add(ted);
}
}
and MainWindow xaml is:
<Grid>
<StackPanel>
<local:myUserControl1 UCItemsSource="{Binding Path=WindowCollection}" />
<Button Content="Refresh" Click="Button_Click" />
</StackPanel>
</Grid>
I am getting empty lines (same as the number of persons) instead of grid with columns. This setup works with native types like long and string types (with TextBox). Could somebody please let me know what I am doing wrong.
I haven't tried this out myself, but the problem seems to be in your myUserControl1 XAML.
Just inside the root Grid is an ItemsControl with the ItemsSource bound to UCItemsSource. This means the ItemsControl will generate one ContentPresenter for each element in that collection- which in your case will be your list of Persons.
Now, inside each of those ContentPresenters, an instance of your DataTemplate will be created. Your DataTemplate contains a DataGrid. This means you will get one entire DataGrid per Person.
If instead you are trying to have one DataGrid with one row per Person, you might want something like this:
<UserControl x:Class="OCasDPdemo.myUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OCasDPdemo"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<DataGrid x:Name="myDataGrid" ItemsSource="{Binding Path=UCItemsSource,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}}}">
</DataGrid>
</Grid>
</UserControl>

WPF DataGrid not loading data

I am a beginner in C# and WPF .
I have created a user control LogTable.atxml which contains a DataGrid and added it to the MainWindow.xaml .
The Table is displayed but the contents are not being fetched.
I think the issue is im not able to sent the Itemsource in the right way.
[Result]Please help.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TableTest.UserControls
{
class Tabledata
{
string A{ get; set; }
string B { get; set; }
string C { get; set; }
public Tabledata(string a, string b, string c)
{
A = a;
B = b;
C =c;
}
}
}
namespace TableTest.UserControls
{
/// <summary>
/// Interaction logic for LogTable.xaml
/// </summary>
public partial class LogTable : UserControl
{
ObservableCollection<Tabledata> list;
public LogTable()
{
InitializeComponent();
list = getTableDetails();
this.logGrid.ItemsSource = list;
}
private ObservableCollection<Tabledata> getTableDetails()
{
ObservableCollection<Tabledata> list= new ObservableCollection<Tabledata>();
Tabledata data = new Tabledata("aaa", "aaa", "aaa");
Tabledata data1 = new Tabledata("bbb", "aaa", "aaa");
Tabledata data2 = new Tabledata("ccc", "aaa", "aaa");
list.Add(data);
list.Add(data1);
list.Add(data2);
return list;
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControls="clr-namespace:TableTest.UserControls" x:Class="TableTest.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<UserControls:LogTable x:Name="logtable" HorizontalAlignment="Left" Margin="0,209,0,0" VerticalAlignment="Top" Width="287" Height="111"/>
</Grid>
</Window>
<UserControl x:Class="TableTest.UserControls.LogTable"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
>
<DataGrid x:Name="logGrid" AutoGenerateColumns="False"
Height="290"
HorizontalAlignment="Left"
VerticalAlignment="Top" Width="290"
ItemsSource="{Binding list}"
>
<DataGrid.Columns >
<DataGridTextColumn Binding="{Binding Path=A}" MinWidth="50" Header="Column 1"/>
<DataGridTextColumn Binding="{Binding Path=B}" MinWidth="50" Header="Column 2"/>
<DataGridTextColumn Binding="{Binding Path=C}" MinWidth="50" Header="Column 3"/>
</DataGrid.Columns>
</DataGrid>
</UserControl>
I think you need to do a few things
1. Your ItemSource needs to bind to a property. So your code should look something like
public partial class LogTable : UserControl
{
public ObservableCollection<Tabledata> list {get;set;}
public LogTable()
{
InitializeComponent();
DataContext=this;
list = new ObservableCollection<TableData>();
list = getTableDetails();
this.logGrid.ItemsSource = list;
}
You need to set your data context of your user control. If you are just using the codebehind you can get away with setting the DataContext in your usercontrols constructor like in the code above. But probably in the future you are going to want to use the mvvm pattern and set your datacontext to your viewmodel.
Note: You will need to set your datacontext of the mainwindow if you want to access any information from that window's codebehind (or whatever you want to bind data from).
Here is a good resource to read up on mvvm.
Update: Just saw your xaml. Since you named the Datagrid you can actually get away with not setting the DataContext as your as setting the ItemSource directly in your code. However, since you don't have your datacontext set you can remove the ItemSource={Binding list} from your xaml. That will only work if you have the list property available on your DataContext.
Update 2: You also need to make your properties public on your TableData class. then it will work
class Tabledata
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public Tabledata(string a, string b, string c)
{
A = a;
B = b;
C = c;
}
}

Binding to 2 Datasources

I want to bind my Datatemplate to 2 Datasources, one datasource that will actually define what is in the ListBox and other that will determine how many ListBoxes are there and what Items in the Listbox are selected\checked.
I have following XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="TokenListTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="chkToken" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<TextBlock Text="{Binding Path=Text}" />
</CheckBox>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ItemTemplate">
<Border BorderThickness="1">
<StackPanel Margin="3">
<TextBlock Text="{Binding Path=Header}"/>
<ListBox ItemTemplate="{StaticResource TokenListTemplate}"
ItemsSource="{Binding Path=Tokens}" >
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
And this is the codebehind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<DataEntity> _actualObjects;
List<Token> tokens1 = new List<Token>()
{
new Token("1"),
new Token("2"),
new Token("3"),
new Token("4")
};
List<Token> tokens2 = new List<Token>()
{
new Token("11"),
new Token("21"),
new Token("31")
};
_actualObjects = new ObservableCollection<DataEntity>()
{
new DataEntity(tokens1, "A", "1,2,3", 1),
new DataEntity(tokens1, "B", "2,3", 1),
new DataEntity(tokens2, "C", "21,31", 2)
};
DataContext = _actualObjects;
}
class DataEntity
{
public DataEntity(List<Token> tokens, string header, string tokenString, int entityTypeId)
{
Tokens = tokens;
Header = header;
TokenString = tokenString;
EntityTypeId = entityTypeId;
}
public List<Token> Tokens { get; set; }
public String Header { get; set; }
public String TokenString { get; set; }
public int EntityTypeId { get; set; }
}
public class Token
{
public bool IsSelected { get; set; }
public string Text { get; set; }
public Token(string text)
{
this.IsSelected = false;
this.Text = text;
}
}
}
It produces this
I don't want to inject token1 or token2 List into DataEntity object so in other words I want DataEntity constructor to be
public DataEntity(string header, string tokenString, int entityTypeId)
Listbox DataTemplate should select
tokens1 List as datasource for its LisBoxItems if
Dataentity.EntityTypeId = 1
tokens2 List as datasource for its LisBoxItemsif
DataEntity.EntityTypeId = 2
Also TokenString in DataEntity should be bound to items in the Listbox i.e. if Listbox shows 1 2 3 4
and DataEntity for this listbox has its TokenString value set to "1,2,3" then 1 2 3 should be checked in the listbox
I would recommend to create a ViewModel as a layer between your model and the view. In the ViewModel you can arrange the data to fit to the used controls without changing your model.
So the ViewModel could for example split the tokenString of the DataEntity into a list of tokens.
Just Google for MVVM (Model-View-ViewModel) for examples and furter explanations or look here on SO (like MVVM: Tutorial from start to finish?).
You're not thinking about this correctly. You need to create one class (some may call a view model) with the responsibility of providing all of the data that the view (or UI) will need. Therefore, you will need to have one property which holds a collection of type DataEntity (if I understand you correctly) to 'define what is in the outer ListBox' as you say.
Then you need a DataTemplate to describe what should be displayed for each item in the ListBox - your 'ItemTemplate' template. This DataTemplate should have another ListBox inside in which to display your Token objects. Your DataEntity should have something like this property in it:
public List<Token> Tokens
{
get
{
if (EntityTypeId == 1) return tokens1;
else if (EntityTypeId == 2) return tokens2;
}
}
You will then need another DataTemplate for your Token objects - your 'TokenListTemplate' template, but without the StackPanel... the inner ListBox replaces that, eg. if there are two Token objects in one DataEntity object, then that object would show two Checkboxes... you have correctly bound the IsChecked property to the Token.IsSelected property.
This may be complicated, but it is entirely possible. Just start with the first layer and get your DataEntity objects displayed in the outer ListBox using your 'ItemTemplate' template. Once that bit is ok, move on to the inner ListBox. Good luck.

ICollectionView filter affects the source

I was experimenting with WPF and encountered some filtering behavior that I did not expect.
I created a simple Window control with a ListView and a DataGrid, which display information about US presidents, such as name, party, and numerical order.
The application instantiates an ObservableCollection with several presidents. In Main, a view is created from the ObservableCollection, and filtering and sorting is applied. The ListView is bound to this view, and the DataGrid is bound to the original ObservableCollection.
I expected the ListView to display the filtered result and the DataGrid to display all the items in the list. However, the DataGrid displays the filtered result as well. Does anyone have an explanation for this?
public partial class MainWindow : Window
{
ICollectionView presidentView;
ObservableCollection<President> presidents = new ObservableCollection<President>
{
new President{Name = "Barack Obama", Party="Democratic", Order=44},
new President {Name = "George W Bush", Party="Republican", Order=43},
new President{Name = "Bill Clinton", Party="Democratic", Order=42},
new President {Name="George Bush", Party="Republican", Order=41},
new President{Name="Ronald Reagan", Party="Republican", Order=40},
new President{Name="Jimmy Carter", Party="Democratic", Order=39},
new President{Name="Gerald Ford", Party="Republican", Order=38},
new President{Name="Richard Nixon", Party="Republican", Order=37},
new President{Name="Lyndon Johnson", Party="Democratic", Order=36}
};
public MainWindow()
{
InitializeComponent();
presidentView = CollectionViewSource.GetDefaultView(presidents);
presidentView.SortDescriptions.Add(new SortDescription("Order", ListSortDirection.Ascending));
Predicate<object> isRepublican = (x) =>
{
President p = x as President;
return p.Party == "Republican";
};
presidentView.Filter = isRepublican;
list.ItemsSource = presidentView;
grid.ItemsSource = presidents;
}
}
public class President
{
public int Order { set; get; }
public string Name { set; get; }
public string Party { set; get; }
}
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="727.416">
<Grid>
<ListView HorizontalAlignment="Left" Height="260" Margin="10,10,0,0" Name="list" VerticalAlignment="Top" Width="197">
<ListView.ItemTemplate>
<ItemContainerTemplate>
<TextBlock Text="{Binding Path=Name}">
</TextBlock>
</ItemContainerTemplate>
</ListView.ItemTemplate>
</ListView>
<DataGrid Name="grid" HorizontalAlignment="Left" Margin="224,13,0,0" VerticalAlignment="Top" Height="257" Width="487"/>
</Grid>
</Window>
CollectionViewSource.GetDefaultView(object) returns the same ICollectionView instance for the given source -- which will be used for any ItemsControl (the DataGrid) when displaying the source collection (presidents).
You can get around this by creating a new instance of ICollectionView to be used by each control that you want independent from other controls (typically, a different one for each different filter).
Update your presidentView to be instantiated like this:
public MainWindow()
{
InitializeComponent();
presidentView = new CollectionViewSource { Source= presidents }.View;
presidentView.SortDescriptions.Add(new SortDescription("Order", ListSortDirection.Ascending));
Predicate<object> isRepublican = (x) =>
{
President p = x as President;
return p.Party == "Republican";
};
presidentView.Filter = isRepublican;
list.ItemsSource = presidentView;
grid.ItemsSource = presidents;
}

Using ItemTemplate for a TreeView when adding items in code

I'm adding TreeViewItems manually in code behind and would like to use a DataTemplate to display them but can't figure out how to. I'm hoping to do something like this but the items are displayed as empty headers. What am I doing wrong?
XAML
<Window x:Class="TreeTest.WindowTree"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowTree" Height="300" Width="300">
<Grid>
<TreeView Name="_treeView">
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=Age}" />
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
Behind code
using System.Windows;
using System.Windows.Controls;
namespace TreeTest
{
public partial class WindowTree : Window
{
public WindowTree()
{
InitializeComponent();
TreeViewItem itemBob = new TreeViewItem();
itemBob.DataContext = new Person() { Name = "Bob", Age = 34 };
TreeViewItem itemSally = new TreeViewItem();
itemSally.DataContext = new Person() { Name = "Sally", Age = 28 }; ;
TreeViewItem itemJoe = new TreeViewItem();
itemJoe.DataContext = new Person() { Name = "Joe", Age = 15 }; ;
itemSally.Items.Add(itemJoe);
_treeView.Items.Add(itemBob);
_treeView.Items.Add(itemSally);
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
Your ItemTemplate is trying to render a "Name" and "Age" property in TextBlocks, but TreeViewItem doesn't have an "Age" property and you aren't setting its "Name".
Because you're using an ItemTemplate, there's no need to add TreeViewItems to the tree. Instead, add your Person instances directly:
_treeView.Items.Add(new Person { Name = "Sally", Age = 28});
The problem, of course, is that your underlying object ("Person") doesn't have any concept of hierarchy, so there's no simple way to add "Joe" to "Sally". There are a couple of more complex options:
You could try handling the TreeView.ItemContainerGenerator.StatusChanged event and wait for the "Sally" item to be generated, then get a handle to it and add Joe directly:
public Window1()
{
InitializeComponent();
var bob = new Person { Name = "Bob", Age = 34 };
var sally = new Person { Name = "Sally", Age = 28 };
_treeView.Items.Add(bob);
_treeView.Items.Add(sally);
_treeView.ItemContainerGenerator.StatusChanged += (sender, e) =>
{
if (_treeView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
var sallyItem = _treeView.ItemContainerGenerator.ContainerFromItem(sally) as TreeViewItem;
sallyItem.Items.Add(new Person { Name = "Joe", Age = 15 });
};
}
Or, a better solution, you could introduce the hierarchy concept into your "Person" object and use a HierarchicalDataTemplate to define the TreeView hierarchy:
XAML:
<Window x:Class="TreeTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowTree" Height="300" Width="300">
<Grid>
<TreeView Name="_treeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Subordinates}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=Age}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
CODE:
using System.Collections.Generic;
using System.Windows;
namespace TreeTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var bob = new Person { Name = "Bob", Age = 34 };
var sally = new Person { Name = "Sally", Age = 28 };
_treeView.Items.Add(bob);
_treeView.Items.Add(sally);
sally.Subordinates.Add(new Person { Name = "Joe", Age = 15 });
}
}
public class Person
{
public Person()
{
Subordinates = new List<Person>();
}
public string Name { get; set; }
public int Age { get; set; }
public List<Person> Subordinates { get; private set; }
}
}
This is a more "data-oriented" way to display your hierarchy and a better approach IMHO.
It will work if you pull your DataTemplate out of the TreeView and put it into Window.Resources. Like this:
<Window.Resources>
<DataTemplate DataType={x:type Person}>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=Age}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
Don't forget to add the right namespace before Person.

Resources