How bind an object to treeview from Xaml - c#

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.

Related

uwp: how to bind data inside DataTemplate outside of x:DataType?

i have MyPage.xaml and MyPage.xaml.cs files.
Into .xaml file i written a data template like this:
<DataTemplate x:Key="MyDataTemplate" x:DataType="local:MyClass">
...
<TextBlock Text="{x:Bind name}" HorizontalAlignment="Left" TextWrapping="Wrap"/>
...
</DataTemplate>
I can bind name attribute of MyClass correctly.
Now i need to bind an attribute of .xaml.cs file but DataTemplate show me only MyClass data. How can i bind data from .xaml.cs page? Outside DataTemplate (but in the same xaml file) i can see any attribute of .xaml.cs file.
I need to bind a List of objects to a combobox like this:
<ComboBox ItemsSource="{x:Bind myList}" HorizontalAlignment="Left"></ComboBox>
but myList is an .xaml.cs attribute.
I want to view a string name attribute of objects of the list.
Thank you for your help
uwp: how to bind data inside DataTemplate outside of x:DataType?
For your recrement, we suggest you use Binding to replace x:Bind, You could use Binding to access current root DataContext with ElementName.
For example
<Grid x:Name="GridRoot">
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<ComboBox ItemsSource="{Binding ElementName=GridRoot, Path=DataContext.Options}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Code Behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
public List<string> Options { get; set; } = new List<string>() {"One","Two","Three" };
public List<Item> Items { get; set; } = new List<Item>()
{
new Item { Name = "HH" },
new Item { Name = "ZZ" },
new Item { Name = "MM" }
};
}
public class Item
{
public string Name { get; set; }
}

WPF recursive treeview with MVVM pattern

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.

Bind collection to Treeview in WPF

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.

TreeView with RadioButtons

I need to create TreeView with dynamicly node creation in my WPF project. There is one condition for TreeView Nodes. All leafs must contains Radiobuttons with work logic same as group of RadioButtons.
I have this XAML code, that describes treeview data template:
<HierarchicalDataTemplate x:Key="sko_ver_hdt">
<RadioButton Margin="0,0,10,0" Content="{Binding Version}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="sko_hdt"
ItemTemplate="{StaticResource sko_ver_hdt}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
I've tried to use CollectionViewSource to group nodes of TreeView like this:
<CollectionViewSource x:Key="cvs" Source="{Binding SKOs}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Version"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
but nothing happens.
I have a model that describes treeview data:
internal class SKOVM
{
public SKOVM(DBCommunication.SKO sko)
{
Name = sko.Name;
Version = sko.VersionCode;
Id = sko.Id;
Children = new ObservableCollection<SKOVM>(sko.SKO1.Select(x => new SKOVM(x)));
sko.SKOSystemInformationReference.Load();
if (sko.SKOSystemInformation != null)
Version = String.Format("{0} / {1}", Version, sko.SKOSystemInformation.Designer);
}
public long Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public ObservableCollection<SKOVM> Children { get; set; }
}
Any ideas how i can implement grouped radiobuttons behaviour logic to my treeview leaf nodes?
Thanks in advance.
Have you tried using the RadioButton.GroupName property?:
<HierarchicalDataTemplate x:Key="sko_ver_hdt">
<RadioButton Content="{Binding Version}" GroupName="{Binding GroupName}" />
</HierarchicalDataTemplate>
This property Gets or sets the name that specifies which RadioButton controls are mutually exclusive. So you'll need a property in the child item class where the child items of each group will have the same value. In this way, you can data bind that value to the GroupName property and the RadioButton of each child item should work in their individual groups.

Dynamically grouping/nesting flat data in a TreeView

I would like to take a flat list of objects and present them in a TreeView using custom groups.
public enum DocumentType { Current, Inactive, Transition, Checkpack, TechLog, Delivery }
public enum Status { Approved, Rejected, Pending }
public class Document
{
public string Name { get; set; }
public DateTime Created { get; set; }
public string CreatedBy { get; set; }
public DateTime Modified { get; set; }
public string ModifiedBy { get; set; }
public DocumentType Type { get; set; }
public Status Status { get; set; }
}
For example... The user might want to see this list, with the top level group being "Status" and the second level being "Name". This all needs to be configurable from the UI, and I'm struggling to find the best way to achieve it.
I've had a brief look at the CollectionViewSource object, but couldn't find a good way to get it to dynamically build a TreeView.
My gut feeling is that i'll need to do some clever templating in XAML - this is as far as i've got...
<Window.Resources>
<DataTemplate x:Key="DocumentTemplate">
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="GroupTemplate"
ItemsSource="{Binding Path=Items}"
ItemTemplate="{StaticResource DocumentTemplate}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding Documents.View.Groups}"
ItemTemplate="{StaticResource GroupTemplate}"/>
</Grid>
public CollectionViewSource Documents
{
get
{
var docs = new CollectionViewSource();
docs.Source = DocumentFactory.Documents;
docs.GroupDescriptions.Add(new PropertyGroupDescription("CreatedBy"));
return docs;
}
}
Of course this only displays the Top-level group ("CreatedBy").
After reading a question below, I managed to come up with a better question...
My question: Is it possible to have a generic HierarchicalDataTemplate for a TreeView that displays custom groups applied to a CollectionViewSource.
Honestly this should be marked as a bug in WPF. I too tried and found that Documents.View.Groups throws binding error on View property being null.
Also
<TextBlock Text="{Binding Path=Name}" />
is correct in the GroupTemplate but not in the DocumentTemplate. Note that Groups are of special type GroupItem where Name is one such property that holds the value on which grouping has taken place.
On the other hand in DocumentTemplate, we should refer the property that we need to display on the leaf nodes items e.g. in my example I used Employee.FirstName (I grouped on Gender).
<DataTemplate x:Key="DocumentTemplate">
<DockPanel>
<TextBlock Text="{Binding FirstName}" />
</DockPanel>
</DataTemplate>
Now for binding to take effect I had to introduce a converter which simply returns Groups.
public class GroupsConverter : IValueConverter
{
public object Convert(object value, ...)
{
return ((CollectionViewSource)value).View.Groups;
}
....
}
And tree view binding was changed this way...
<TreeView x:Name="treeView"
ItemsSource="{Binding Path=Documents,
Converter={StaticResource GroupsConverter}}"
ItemTemplate="{StaticResource GroupTemplate}" />
Then this worked for me.
Does this help you?

Categories

Resources