is there a way to use commands in a treeview with a HierarchicalDataTemplate, so that i can react on the click on an item in the treeview?
i would prefer a solution without code-behind if there is one.
Here is my TreeView:
<TreeView ItemsSource="{Binding Main.TreeItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Header}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
provide an property of type ICommand on your Item (ViewModel) and Bind to that. So your item class would be something like:
class MyTreeItem
{
public MyTreeItem()
{
this.SomeCommand = /* create command here */ null;
this.Children = new ObservableCollection<MyTreeItem>();
}
public ICommand SomeCommand { get; private set; }
public ObservableCollection<MyTreeItem> Children { get; private set; }
}
In xaml you can then write:
<TreeView ItemsSource="{Binding Main.TreeItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{TemplateBinding Header}" />
<Button Text="My Command" Command="{TemplateBinding SomeCommand}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I think that should work, at least it did similarily last time i used that :)
Related
My model looks like
public class MyVm
{
public string MyTitle { get; set; }
public List<MyVm> Children { get; set; }
public MyVm()
{
this.Children = new List<MyVm>();
}
}
I want to be able to list through all the children and children's children, which I think is recursive.
The MainWindow code behind is
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Kids = new List<MyVm>();
var m = new MyVm();
m.MyTitle = "Title1";
var m2 = new MyVm();
m2.MyTitle = "Title2";
var m3 = new MyVm();
m3.MyTitle = "Title3";
var m4 = new MyVm();
m4.MyTitle = "Title4";
m.Children.Add(m2);
m2.Children.Add(m3);
m3.Children.Add(m4);
this.Kids.Add(m);
}
public List<MyVm> Kids { get; set; }
and finally the MainWIndow view is
<Grid.Resources>
<Style x:Key="MyStyle" TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<HierarchicalDataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MyTitle}" />
<ListView ItemsSource="{Binding Children}" ItemContainerStyle="{Binding MyStyle}" />
</StackPanel>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ListView ItemsSource="{Binding Kids}" ItemContainerStyle="{StaticResource MyStyle}" />
As you can see I've tried to re-use the same resource for each 'children' to achieve the recursive bit, but sadly, the only thing I see rendered is a single TextBlock with Title2
For these purposes you would use a HierarchicalDataTemplate (which has its own ItemsSource property that you would bind to Children), i am not sure if a ListBox supports it. If not use a TreeView and change the control templates to remove the indentation and the collapse toggle button if you don't want that.
Fixed it
<Grid.Resources>
<DataTemplate DataType="{x:Type a:MyVm}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MyTitle}" />
<ListView ItemsSource="{Binding Children}">
<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding }" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ListView ItemsSource="{Binding Kids}" />
I have built a DataTemplateSelector which I use for showing a different pushpin on the map. I now have the following DataTemplate wich works in the way I want, except that it are not pushpins but textblocks which are shown on the map.
<DataTemplate x:Key="pushpinSelector">
<my:Pushpin Location="{Binding Location}" Tap="Pushpin_Tap">
<my:Pushpin.Template>
<ControlTemplate>
<local:PushpinTemplateSelector Content="{Binding}">
<local:PushpinTemplateSelector.ClusterTemplate>
<DataTemplate>
<TextBlock Text="Cluster" Width="100" Foreground="YellowGreen"></TextBlock>
</DataTemplate>
</local:PushpinTemplateSelector.ClusterTemplate>
<local:PushpinTemplateSelector.PushpinTemplate>
<DataTemplate>
<TextBlock Text="Pushpin" Width="100" Foreground="Blue"></TextBlock>
</DataTemplate>
</local:PushpinTemplateSelector.PushpinTemplate>
</local:PushpinTemplateSelector>
</ControlTemplate>
</my:Pushpin.Template>
</my:Pushpin>
</DataTemplate>
I would expect that it would work in the following format too:
<DataTemplate x:Key="pushpinSelector">
<local:PushpinTemplateSelector Content="{Binding}">
<local:PushpinTemplateSelector.ClusterTemplate>
<DataTemplate>
<my:Pushpin Location="{Binding Location}" Content="{Binding Count}" Foreground="YellowGreen"></my:Pushpin>
</DataTemplate>
</local:PushpinTemplateSelector.ClusterTemplate>
<local:PushpinTemplateSelector.PushpinTemplate>
<DataTemplate>
<my:Pushpin Location="{Binding Location}" Foreground="Blue"></my:Pushpin>
</DataTemplate>
</local:PushpinTemplateSelector.PushpinTemplate>
</local:PushpinTemplateSelector>
</DataTemplate>
But with this template it's only showing 1 black pushpin on the map. Am I doing something wrong with the Bindings? I'am not seeing why this is not working in the expected way.
As requested by #localjoost the code for the datatemplateselector:
The abstract class:
public abstract class DataTemplateSelector : ContentControl
{
public virtual DataTemplate SelectTemplate(
object item, DependencyObject container)
{
return null;
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
ContentTemplate = SelectTemplate(newContent, this);
}
}
And the implementation of the abstract class:
public class PushpinTemplateSelector : DataTemplateSelector
{
public DataTemplate ClusterTemplate
{
get;
set;
}
public DataTemplate PushpinTemplate
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
PushpinModel model = item as PushpinModel;
if (model.CurrentObject == null)
{
return ClusterTemplate;
}
else
{
return PushpinTemplate;
}
}
}
And how I use the Datatemplate (which is defined in my application resources section) in my map control:
<my:Map Height="624" HorizontalAlignment="Left" Name="map1" VerticalAlignment="Top" Width="468" CredentialsProvider="XXXXX"
ZoomLevel="13">
<my:MapItemsControl Name="pushPinModelsLayer" ItemsSource="{Binding PushpinModels}" ItemTemplate="{StaticResource pushpinSelector}" />
<my:Pushpin Name="myLocation" Template="{StaticResource MyLocationTemplate}"></my:Pushpin>
</my:Map>
As far is I can see there's not rocket science inside the pushpinselectortemplate.
I usually do this using multiple layers. This is a piece of code that comes from an actual working program. Maybe adding a Layer helps you out?
<Microsoft_Phone_Controls_Maps:Map x:Name="map"
CredentialsProvider="blah">
<Microsoft_Phone_Controls_Maps:MapLayer x:Name="MapLayer_GasStations">
<Microsoft_Phone_Controls_Maps:MapItemsControl
ItemsSource="{Binding GasStations}"
ItemTemplate="{StaticResource GasStationViewModelTemplate}" />
</Microsoft_Phone_Controls_Maps:MapLayer>
<Microsoft_Phone_Controls_Maps:MapLayer x:Name="MapLayer_RoadBlocks">
<Microsoft_Phone_Controls_Maps:MapItemsControl
ItemsSource="{Binding RoadBlocks}"
ItemTemplate="{StaticResource RoadBlockViewModelTemplate}" />
</Microsoft_Phone_Controls_Maps:MapLayer>
</Microsoft_Phone_Controls_Maps:Map>
Haven't found a different approach than the first one described in the question. For now I will work with it in that way.
I am trying to create an application in which i require to display employees and their departments in the treeview kind of structure as below :
Employee1
Department
Dept1
Dept2
Employee2
Department
Dept3
Dept4
how could i do this with WPF ?
The correct way to do this is to use a HierarchicalDataTemplate. The most basic one I can imagine is the following:
<UserControl.Resources>
<HierarchicalDataTemplate
x:Key="RecursiveData" DataType="TreeViewItem" ItemsSource="{Binding Items}">
</HierarchicalDataTemplate>
</UserControl.Resources>
Which can be used in the XAML as follows:
<TreeView ItemTemplate="{StaticResource RecursiveData}" />
Of course you can customize the template at will with styles and subcomponents.
Note that the ItemSource of your TreeView needs to actually provide nested TreeViewItems where each TreeViewItem contains it's subitems in Items.
If you've structure like this:
public ObservableCollection<ChartOfAccount> ChartOfAccounts { get; set; }
public class ChartOfAccount
{
public Book Book { get; set; }
public List<LedgerHierarchy> ControlLedgers { get; set; }
}
public class LedgerHierarchy
{
public ControlLedger ControlLedger { get; set; }
public ObservableCollection<Ledger> Ledgers { get; set; }
}
you could bind directly in TreeView like this:
<TreeView ItemsSource="{Binding ChartOfAccounts}"
BorderThickness="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemContainerStyle="{StaticResource treeStyle}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ControlLedgers}">
<TextBlock Text="{Binding Book.Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Ledgers}">
<TextBlock Text="{Binding ControlLedger.Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
instead of creating HierarchicalDataTemplate in Control.Resource.
I have a window MainWindow.xaml and
private static Tutorial tutorial; there.
Also I have class Structure.cs where I describe child types
public class Tutorial
{
public string Name { get; set; }
public IList<Chapter> Chapters = new List<Chapter>();
}
public class Chapter
{
public string Name { get; set; }
public IList<Unit> Units = new List<Unit>();
}
public class Unit
{
public string Name { get; set; }
public IList<Frame> Frames = new List<Frame>();
...
}
I want to bind tutorial structure to treeview. How can I do this?
I tried this way.
<TreeView Grid.Row="2" x:Name="treeViewStruct" Margin="5,0,5,0" Background="LemonChiffon" BorderBrush="Bisque" BorderThickness="1" ScrollViewer.VerticalScrollBarVisibility="Auto" IsTextSearchEnabled="True" Cursor="Hand">
<TreeView.Resources>
<HierarchicalDataTemplate DataType = "{x:Type Structure:Chapter}"
ItemsSource = "{Binding Path=Units}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type Structure:Unit}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
It doesn't work.
Please, help! I'm a newbie in WPF. I need dynamic tree
so that when I add a chapter or a unit in the object tutorial, tree is updated.
And for this way of binding please throw the idea how can I get a collection item, when I selected some tree node.
This may help :
<HierarchicalDateTemplate DataType = "{x:Type local:Tutorial}"
ItemsSource="{Binding Chapters}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDateTemplate>
<HierarchicalDateTemplate DataType = "{x:Type local:Chapter}"
ItemsSource="{Binding Units}"
<TextBlock Text="{Binding Name}"/>
</HierarchicalDateTemplate>
<DateTemplate DataType = "{x:Type local:Unit}"
<TextBlock Text="{Binding Name}"/>
</DateTemplate>
I am trying to bind some data from a class instance to a TreeView. My code is as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Parent myClass = new Parent();
this.DataContext = myClass;
}
}
public class Parent
{
private List<string> children = new List<string>;
public string Name {get;set;}
public List<string> Children
{
get { return this.children; }
set { this.children=value; }
}
public Parent()
{
this.Name = "Test";
for (int i = 1; i <= 10; i++)
{
Children.Add(i.ToString());
}
}
}
And the XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WpfApplication1"
Title="MainWindow" Height="287" Width="525">
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch">
<TreeView Name="TreeView" ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
</Window>
Nothing shows up in my TreeView. What am I doing wrong?
I expect to see:
-Test
-- 1
-- 2
-- 3
etc
updated: to make Name and Children properties instead of public fields.
updated: added what I expect to see in TreeView
You bind "myClass" of type Parent to the datacontext and use the same object for the ItemSource. This won't work because Parent is not an enumerable type.
Change your ItemSource binding from "{Binding}" to "{Binding Children}" and change your HierarchicalDataTemplate to just a DataTemplate.
If you want to retain myClass as a top level node, you could also wrap myclass in an IEnumerable and set that as the data context.
You can only bind to public properties, not fields. Change Name and Children to be public properties and this should work.
Here try this:
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch">
<TextBlock Text="{Binding Name}"/>
<TreeView Name="TreeView" ItemsSource="{Binding Children}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
A few things:
The 'Name' Property was out of scope.
We need to bind the TreeView as well as the DataTemplate
We use the default {Binding} to call
ToString() on the current item.