I'm trying to bind a collection to a treeview. My attempt so far have failed.
I miss something despite the articles I read about the matter.
So far I tried the something like, but the Treeview just plot the Id of class A and thats it, with no button to expand.
<Grid>
<TreeView HorizontalAlignment="Left" Height="270" VerticalAlignment="Top" Width="292" ItemsSource="{Binding ManagerObjects}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ManagerObject}" ItemsSource="{Binding MyManager}">
<TextBlock Text="{Binding Id}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Manager}" ItemsSource="{Binding ManagerClientServerProperty}">
<TextBlock Text="{Binding ManagerClientServerProperty}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local1:ManagerClientServer}">
<TextBlock Text="TEST"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local1:NetworkObject}" ItemsSource="{Binding Entities}">
<TextBlock Text="TEST"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local1:RemoteEntity}" ItemsSource="{Binding Fields}">
<TextBlock Text="TEST"/>
<!-- how classD should look like -->
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
EDIT: ADDING REAL CODE
This is in my Model :
public class ManagerObject
{
// PROPERTIES
public int Id { get; private set; }
public Manager MyManager { get; private set; }
}
public class Manager
{
// FIELDS
private readonly ManagerClientServer managerClientServer;
// PROPERTIES
public ManagerClientServer ManagerClientServerProperty { get { return managerClientServer;} }
/**** OTHER STUFF NON IMPORTANT ****/
}
public class ManagerClientServer
{
// FIELDS
private readonly ObservableCollection<NetworkObject> Clients = new ObservableCollection<NetworkObject>();
private readonly ObservableCollection<NetworkObject> Servers = new ObservableCollection<NetworkObject>();
// PROPERTIES
public ObservableCollection<NetworkObject> ClientsProperty
{
get { return Clients; }
}
public ObservableCollection<NetworkObject> ServersProperty
{
get { return Servers; }
}
/*** OTHER STUFF NON IMPORTANT HERE ***/
}
public class NetworkObject
{
// FIELDS
private readonly ObservableCollection<RemoteEntity> _entities=new ObservableCollection<RemoteEntity>();
public uint NetworkId { get; private set; }
// PROPERTIES
public ObservableCollection<RemoteEntity> Entities
{
get { return _entities; }
}
// CONSTRUCTOR
public NetworkObject(uint id)
{
NetworkId = id;
}
}
public class RemoteEntity
{
// FIELDS
private readonly ObservableCollection<int> _fields=new ObservableCollection<int>();
// PROPERTIES
public bool IsLost { get; set; }
public bool NeedUpdate { get; set; }
public uint SessionId { get; private set; }
public ObservableCollection<int> Fields
{
get { return _fields; }
}
// CONSTRUCTOR
public RemoteEntity(uint id)
{
SessionId = id;
}
}
The ViewModel just expose this property:
public ObservableCollection<ManagerObject> ManagerObjects
{
get { return managerObjects; }
set
{
managerObjects = value;
NotifyOfPropertyChange(()=>ManagerObjects);
}
}
private ObservableCollection<ManagerObject> managerObjects;
The initialization is just 2 ManagerObject, after this they all include a random number of NetworkObjects in both Clients and Servers collection and each of those has a random number of Entities.
All collections here are Observable, however, they are of another type in real but they expose a method which can make them Observable so lets consider it this way.
Many Thanks.
Ah, I see your problem now and it's a really simple one. You can't expand anything because there is nothing to expand. Your TreeView.ItemsSource is bound to the ManagerObjects collection and that's ok, because it is a collection. However, in your HierarchicalDataTemplate for your ManagerObject data type, you have this:
<HierarchicalDataTemplate DataType="{x:Type local:ManagerObject}"
ItemsSource="{Binding MyManager}"> <!-- Look here -->
<TextBlock Text="{Binding Id}"/>
</HierarchicalDataTemplate>
You are trying to data bind the MyManager property to the HierarchicalDataTemplate.ItemsSource property, but you can't because it is not a collection. Instead, try this:
<DataTemplate DataType="{x:Type local:ManagerObject}">
<StackPanel>
<TextBlock Text="{Binding Id}" />
<ContentControl Content="{Binding MyManager}" />
</StackPanel>
</DataTemplate>
You'll have other problems like this too, so you'll have to adjust a number of your templates. For example, this won't work because the ManagerClientServerProperty property is not a collection:
<HierarchicalDataTemplate DataType="{x:Type local:Manager}"
ItemsSource="{Binding ManagerClientServerProperty}">
...
</HierarchicalDataTemplate>
You could do this:
<HierarchicalDataTemplate DataType="{x:Type local:Manager}"
ItemsSource="{Binding ManagerClientServerProperty.Clients}">
...
</HierarchicalDataTemplate>
... but then that would only be one of the collections. When writing WPF, I've learned that it's always best to make your data the right shape to fit your UI. Usually, that just means adding a few extra properties here and there to make your job displaying it easier. For example, instead of using a CompositeCollection in the UI, you could just add an extra property to your ManagerClientServer class. Maybe something like this:
public ObservableCollection<NetworkObject> NetworkObjects
{
get
{
hhh networkObjects = new ObservableCollection<NetworkObject>(Clients);
networkObjects.Add(Servers);
return networkObjects;
}
}
Then you could do this:
<HierarchicalDataTemplate DataType="{x:Type local:Manager}"
ItemsSource="{Binding ManagerClientServerProperty.NetworkObjects}">
...
</HierarchicalDataTemplate>
Anyway, I guess you get the picture now, so I trust that you can finish the rest on your own. Oh, one last thing... don't be surprised if it won't work, because your data is not in the correct 'shape' that a TreeView expects. It might work, but if not, forget the HierarchicalDataTemplates and just define ListBoxes in DataTemplates to bind to the inner collections.
Related
I have a treeView defined in XAML as:
<UserControl.Resources>
<models:TreeLines x:Key="myLines" x:Name="myLinesData"/>
</UserControl.Resources>
<TreeView
x:Name="treeData"
Grid.Column="1"
Padding="0,5,0,0"
Background="#282828"
BorderThickness="0"
SelectedValuePath="Uid">
<TreeViewItem
x:Name="tLines"
Uid="tabLines"
Header="Lines"
ItemsSource="{Binding Source={StaticResource myLines}, Path=MyLines}"
Style="{StaticResource custTVItem}">
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Lines}" ItemsSource="{Binding lineSet}">
<TextBlock Text="{Binding productName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:LineSets}" ItemsSource="{Binding lineName}">
<TextBlock Text="{Binding setName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:LineNames}" ItemsSource="{Binding dataTypes}">
<TextBlock Text="{Binding lineName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:LineData}" ItemsSource="{Binding dataVals}">
<TextBlock Text="{Binding dataType}" />
</HierarchicalDataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
</TreeView>
The UserControl.Resources is pointing towards a class:
public partial class TreeLines : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(MainWindow.treeData.ItemsSource))]
private List<Lines>? myLines;
}
The error I get here is:
The target(s) of [NotifyPropertyChangedFor] must be a (different) accessible property
The object myLines I'm trying to bind to has the classes behind, as seen in the TreeView `HierarchicalDataTemplates:
public class Lines
{
public string productName { get; set; }
public List<LineSets> lineSet { get; set; }
}
public class LineSets
{
public string setName { get; set; }
public List<LineNames> lineName { get; set; }
}
public class LineNames
{
public string lineName { get; set; }
public List<LineData> dataTypes { get; set; }
}
public class LineData
{
public string dataType { get; set; }
public List<double> dataVals { get; set; }
}
If I remove all the CommunityToolkit.MVVM aspects and set my variable:
private List<Lines>? myLines; manually by changing it to public and assigning data to it on loading, then it populates on load only.
I need to modify myLines on the fly within my C# code which in-turn should update the treeView. You can see I'm trying to achieve this automatically with the data binding but something isn't right.
I think the mistakes could possibly be in the line:
[NotifyPropertyChangedFor(nameof(MainWindow.treeData.ItemsSource))]
and/or possibly the StaticResource usage in XAML:
<TreeViewItem
x:Name="tLines"
Uid="tabLines"
Header="Lines"
ItemsSource="{Binding Source={StaticResource myLines}, Path=linesCollection}"
Style="{StaticResource custTVItem}">
Please advise if you can help
Replace all List<T> properties with ObservableCollection<T>. Then the view will be updated whenever you add or remove items from these collections.
For the view to also update when you change a property of an individual item in a collection, the class of the property that you change should implement the INotifyPropertyChanged interface and raise change notifications.
Here is an example of how you should implement the Lines class:
public class Lines : ObservableObject
{
[ObservableProperty]
private string productName { get; set; }
[ObservableProperty]
private ObservableCollection<LineSets> lineSet { get; set; }
}
Bind to the generated properties (starting with an uppercase letter):
<HierarchicalDataTemplate DataType="{x:Type models:Lines}"
ItemsSource="{Binding LineSet}">
<TextBlock Text="{Binding ProductName}"/>
</HierarchicalDataTemplate>
[NotifyPropertyChangedFor(nameof(MainWindow.treeData.ItemsSource))] does not need to be added.
There is no need to implement additional notifications. Because [ObservableProperty] is already implementing the notification function.
Check out the auto-generated sources.
[NotifyPropertyChangedFor(parameter)]'s parameter should be the name of property inside the class.
public partial class TreeLines : ObservableObject
{
[ObservableProperty]
private List<Lines>? myLines;
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
}
In this case, the possible Arguments of [NotifyPropertyChangedFor] are only MyLines, OtherProperty1 , and OtherProperty2.
[NotifyPropertyChangedFor] is an attribute indicating that other properties connected within the class have changed
Here's an example.
public partial class GetSum : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Sum))]
private int num1;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Sum))]
private int num2;
public int Sum { get => num1 + num2; }
}
When calling the setter of Num1 Property,
simultaneously update the Num1 value and Sum value bound to the screen.
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp2"
Height="450" Width="800">
<Window.DataContext>
<local:GetSum/>
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Num1}"/>
<TextBox Text="{Binding Num2}"/>
<TextBlock Text="{Binding Sum}"/>
</StackPanel>
</Window>
i'm new to WPF and MVVM pattern. I'm trying to bind recursively a Treeview to ObservableCollections.
I have searched so many times on this site, but I found no answers to my problem.
Here it is my Model class:
public class CategoryCounter
{
public int ID { get; set; }
public string Supplier { get; set; }
public string Name { get; set; }
public Nullable<int> Parent { get; set; }
public ObservableCollection<CategoryCounter> Children => new ObservableCollection<CategoryCounter>(/*some linq code here*/);
And the ViewModel class:
public class CategoriesViewModel : BaseViewModel
{
private string supplier;
private ObservableCollection<CategoryCounter> categories;
public ObservableCollection<CategoryCounter> Categories
{
get { return categories; }
set
{
if (value != categories)
{
categories = value;
}
}
}
public void SetSupplier(string supplier)
{
this.supplier = supplier;
Categories = new ObservableCollection<CategoryCounter>(CategoryContatori.GetRootBySupplier(supplier));
NotifyPropertyChanged();
}
}
Now, when i call "SetSupplier()" the collection is filled and it is all ok, but the view does not show me anything.
This is the XAML code:
<StackPanel>
<TreeView ItemsSource="{Binding Categories}" HorizontalAlignment="Left" Height="200" VerticalAlignment="Top" Width="250">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type dbModel:CategoryCounter}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</StackPanel>
How can I bind the children items even if they are the same object? Is this the problem?
Thank you for your patience.
Try setting the template directly
<TreeView ItemsSource="{Binding Categories}" HorizontalAlignment="Left" Height="200" VerticalAlignment="Top" Width="250">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Resources generally need to be in the parent of the item that needs to access them.
EDIT: Although having just done a quick search this may not be the case for TreeViews, so it's likely that INPC is your problem as already noted in the comments.
Raise the PropertyChanged in the setter of your Categories property:
private ObservableCollection<CategoryCounter> categories;
public ObservableCollection<CategoryCounter> Categories
{
get { return categories; }
set
{
categories = value;
NotifyPropertyChanged(nameof(Categories));
}
}
Also make sure that the Categories property and the Children property return materialized collections of CategoryCounter objects.
I have a custom object that consists of 2 properties. The first is a string that i wish to use as a summary or header in the tree view. The second is a list of a custom type that contains objects that are to be included under each header. The objects contain things such as name, id, area, etc.. Ill most likely default to the name property of those list objects. How can I push this into a tree view.
Concatenated Model
public class WVWellModel : Notifier
{
private string _API;
public string API
{
get
{
return this._API;
}
set
{
this._API = value; OnPropertyChanged("API");
}
}
private string _WellName;
public string WellName
{
get
{
return this._WellName;
}
set
{
this._WellName = value; OnPropertyChanged("WellName");
}
}
private string _Division;
public string Division
{
get
{
return this._Division;
}
set
{
this._Division = value; OnPropertyChanged("Dvision");
}
}
private string _Area;
public string Area
{
get
{
return this._Area;
}
set
{
this._Area = value; OnPropertyChanged("Area");
}
}
private string _FieldOffice;
public string FieldOffice
{
get
{
return this._FieldOffice;
}
set
{
this._FieldOffice = value; OnPropertyChanged("FieldOffice");
}
}...............
** Model that will be put in a list to be injected into tree view**
public class groupingModel : Notifier
{
private string _Header;
public string Header
{
get { return _Header; }
set { _Header = value; OnPropertyChanged("Header"); }
}
private List<WVWellModel> _Wells;
public List<WVWellModel> Wells
{
get { return _Wells; }
set { _Wells = value; OnPropertyChanged("Wells"); }
}
}
List of Custom Type to be injected into tree view
List treeViewList = someMethod();
In summary, I would like to bind my tree view to a custom list object.List<groupingModel> The object in those lists have two properties, a string header that is to be used to group the objects in the tree view, and a second property that contains a list of custom objects "WVWellModel".
EDIT TO XAML to Allow Selection of all items in group
I've attempted to go ahead and make the group selectable with he goal that if the group is selected all children are selected underneath. Ive successfully bound it to a property inside of the group called "IsChecked". it defaults to false and works successfully. The problem is i am unable to capture the change in value and thus cannot run any logic to select its children.
<TreeView DockPanel.Dock="Bottom" ItemsSource="{Binding Groups}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="wellModel:WellGroupModel" ItemsSource="{Binding Wells}">
**<CheckBox Content="{Binding Header}" IsChecked="{Binding IsChecked}"/>**
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type wellModel:WellModel}">
<CheckBox Content="{Binding WellName}" IsChecked="{Binding IsSelected}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The TreeView control uses HierarchicalDataTemplate to control how items are displayed and how their children are populated. If your item class has children of a different type, it can specify its own child ItemTemplate, and so on recursively.
I've also added a minimal top-level viewmodel which owns a collection of GroupingModel. I'm using conventional C# naming conventions: Classes and properties start with a capital letter, private fields start with an underscore and a lower-case letter. It seems silly but when everybody uses the same convention, you always know what you're looking at.
Finally, I used ObservableCollection<T> rather than List<T>. If you bind an ObservableCollection to a control, then you can add/remove items in the collection and the control will automatically be notified and update itself without any additional work on your part.
XAML
<TreeView
ItemsSource="{Binding Groups}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupingModel}"
ItemsSource="{Binding Wells}"
>
<TextBlock Text="{Binding Header}" />
<HierarchicalDataTemplate.ItemTemplate>
<!-- This can be DataTemplate if no child collection is specified -->
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<TextBlock Text="{Binding WellName}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Alternatively, if you have heterogeneous collections of objects, you can create implicit templates as resources and let them be applied by type rather than by hierarchy. In your particular case, this will produce identical results, because you have a strict item hierarchy.
<TreeView
ItemsSource="{Binding Groups}"
>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupingModel}"
ItemsSource="{Binding Wells}"
>
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<TextBlock Text="{Binding WellName}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
C#
public class ViewModel : Notifier
{
public ViewModel()
{
Groups = new ObservableCollection<GroupingModel>
{
new GroupingModel {
Header = "First Group",
Wells = new List<WVWellModel> {
new WVWellModel() { WellName = "First Well" },
new WVWellModel() { WellName = "Second Well" },
new WVWellModel() { WellName = "Third Well" },
}
},
new GroupingModel {
Header = "Second Group",
Wells = new List<WVWellModel> {
new WVWellModel() { WellName = "Third Well" },
new WVWellModel() { WellName = "Fourth Well" },
new WVWellModel() { WellName = "Fifth Well" },
}
}
};
}
#region Groups Property
private ObservableCollection<GroupingModel> _groups = new ObservableCollection<GroupingModel>();
public ObservableCollection<GroupingModel> Groups
{
get { return _groups; }
set
{
if (value != _groups)
{
_groups = value;
OnPropertyChanged(nameof(Groups));
}
}
}
#endregion Groups Property
}
Update
Let's make the WVWellModel items checkable. First, we'll give them a boolean property that we'll bind to the checkbox's IsChecked property:
public class WVWellModel : Notifier
{
private bool _isSelected;
public bool IsSelected
{
get
{
return this._isSelected;
}
set
{
this._isSelected = value; OnPropertyChanged();
}
}
And then we'll change the content in the WVWellModel DataTemplate from a TextBlock to a CheckBox:
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<CheckBox
Content="{Binding WellName}"
IsChecked="{Binding IsSelected}"
/>
</DataTemplate>
You can put any valid XAML UI in a template as long as there's a single root element.
<TreeView
Width="300"
Height="200"
ItemsSource="{Binding Groups}"
Grid.IsSharedSizeScope="True"
>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupingModel}"
ItemsSource="{Binding Wells}"
>
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="CheckBoxColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="APIColumn" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.Column="0"
Content="{Binding WellName}"
IsChecked="{Binding IsSelected}"
/>
<TextBlock
Grid.Column="1"
Margin="12,0,0,0"
Text="{Binding API}"
HorizontalAlignment="Right"
/>
</Grid>
</DataTemplate>
</TreeView.Resources>
</TreeView>
I am trying to bind an object to the treeviewcontrol WPF by XAML, I am getting the treview as empty. When i am doing that by treeview.items.add(GetNode()) then its working.
I am using MVVM Framework(caliburn.Micro) I wanted to do it in Xaml, how do I assign Item source property in xaml? I tried with creating a property of Node class and calling the Method GetNode() with in the property, and assigned that property as itemssource of the treeview and changed the List to Observable collection. Still issue is same.
Working Xaml when doing treeview.items.Add(GetNode()) which returns a Node and and i as assigning Nodes collection to Hireachial Template.
<TreeView Name="treeview2"
Grid.RowSpan="2"
Grid.ColumnSpan="2"
ItemContainerStyle="{StaticResource StretchTreeViewItemStyle}" Width="300">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<DockPanel LastChildFill="True">
<TextBlock Padding="15,0,30,0" Text="{Binding Path=numitems}" TextAlignment="Right"
DockPanel.Dock="Right"/>
<TextBlock Text="{Binding Path=Text}" DockPanel.Dock="Left" TextAlignment="Left" />
</DockPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Server Side Code:
this.treeview2.Items.Add(GetNode());
GetNode recursively build a list of type Node.
public class Node
{
public string Text { get; set; }
public List<Node> Nodes { get; set; }
public ObservableCollection<Node> Nodes{get;set;} // with list and observable collection same results
public int numitems { get; set; }
}
In addition to the HierarchicalDataTemplate, which seems just fine, add a binding to the ItemsSource property of your TreeView:
public class ViewModel
{
private List<Node> _rootNodes;
public List<Node> RootNodes
{
get
{
return _rootNodes;
}
set
{
_rootNodes = value;
NotifyPropertyChange(() => RootNodes);
}
}
public ViewModel()
{
RootNodes = new List<Node>{new Node(){Text = "This is a Root Node}",
new Node(){Text = "This is the Second Root Node"}};
}
And in XAML:
<TreeView ItemsSource="{Binding RootNodes}"
.... />
Edit: Remove the call that does this.Treeview.... you don't need that. Try to keep to the minimum the amount of code that references UI Elements. You can do everything with bindings and have no need to manipulate UI Elements in code.
Could you tell me why I can't see subItems?
I've got winforms apps and I added my wpfusercontrol:ObjectsAndZonesTree
ServiceProvider is my webservice. Adn method to get listofcountires with subitems works properly (i get countires, regions from this countires, provinces etc...)
ElementHost elementHost = new ElementHost
{
Width = 150,
Height = 50,
Dock = DockStyle.Fill,
Child = new ObjectsAndZonesTree()
};
this.splitContainer3.Panel1.Controls.Add(elementHost);
XAML:
<TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type ServiceProvider:Country
}" ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ServiceProvider:Region}" >
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
<DataTemplate DataType="{x:Type ServiceProvider:Province}" >
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
XAML.CS
public ObjectsAndZonesTree()
{
InitializeComponent();
LoadView();
}
private void LoadView()
{
GroupView.ItemsSource = new ServiceProvider().GetListOfObjectsAndZones();
}
class Country:
public class Country
{
string _name;
[XmlAttribute]
public string Name
{
get { return _name; }
set { _name = value; }
}
string _code;
[XmlAttribute]
public string Code
{
get { return _code; }
set { _code = value; }
}
string _continentCode;
[XmlAttribute]
public string ContinentCode
{
get { return _continentCode; }
set { _continentCode = value; }
}
public Region[] ListOfRegions
{
get { return _listOfRegions; }
set { _listOfRegions = value; }
}
private Region[] _listOfRegions;
public IList<object> Items
{
get
{
IList<object> childNodes = new List<object>();
foreach (var group in this.ListOfRegions)
childNodes.Add(group);
return childNodes;
}
}
}
Class Region:
public class Region
{
private Province[] _listOfProvinces;
private string _name;
private string _code;
public Province[] ListOfProvinces
{
get { return _listOfProvinces; }
set { _listOfProvinces = value; }
}
public string Name
{
get {
return _name;
}
set {
_name = value;
}
}
public string Code
{
get {
return _code;
}
set {
_code = value;
}
}
public string CountryCode
{
get { return _countryCode; }
set { _countryCode = value; }
}
private string _countryCode;
public IList<object> Items
{
get
{
IList<object> childNodes = new List<object>();
foreach (var group in this.ListOfProvinces)
childNodes.Add(group);
return childNodes;
}
}
}
It displays me only list of countires.
Your Region DataTemplate needs to be a HierarchicalDataTemplate to support nested items (SubItems). You also need to specify it's ItemsSource.
<TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type ServiceProvider:Country}"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type ServiceProvider:Region}"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ServiceProvider:Province}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
So for example if you add Cities to your Provinces the changes in your XAML might look something like this.
<HierarchicalDataTemplate DataType="{x:Type ServiceProvider:Province}"
ItemsSource="{Binding Cities}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ServiceProvider:City}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
Not sure where your problem is, but I thought I would share you the best resource I've found when dealing with Treeview. Thoses extension methods saved me a lot of hassle :
http://www.scip.be/index.php?Page=ArticlesNET23
They transform any flat list into a Ienumerable of HierarchyNode using some nice lambda syntax. It is implemented with IQueryable, which means efficient even against a linq datacontext.
Have you implemented INotifyPropertyChanged on Binding source class??
Plus you can check for binding exceptions in output window of visual studio.
It will help you understanding invalid bindings.
You'll need HierarchialDataTemplates instead of plain DataTemplates.
The others wrote everything, so I'll post some useful links:
http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx
http://blogs.msdn.com/mikehillberg/archive/2006/10/11/a-treeview-a-hierarchicaldatatemplate-and-a-2d-collection-walk-into-a-bar.aspx
http://msdn.microsoft.com/en-us/library/system.windows.hierarchicaldatatemplate.aspx