How to notify ViewModel when properties binded to an ObservableCollection change? - c#

I'm using a HierarchicalDataTemplate to build a menubar that will have MenuItems that are checkable. In my ViewModel, I create an ObservableCollection of a class called MenuItemModel, then bind the ObservableCollection in my View. I can build the menu along with its submenus, but I can't figure out how to tell the ViewModel which MenuItem is checked.
I've tried using the INotifyPropertyChanged in the MenuItemModel but I could not figure out how to send that information to the ViewModel. After much googling, I've come to the conclusion this isn't the proper approach and I only need to use the INotifyPropertyChanged in the ViewModel. I'm a WPF newbie so still learning the do's and don'ts. I've found most of the code below on StackOverflow and have managed to adapt it to to my needs but I'm still trying to wrap my head around how it works. The code below will create a menu "Main Menu" with 3 submenus "SubMenu1", "SubMenu2", and "SubMenu3" where they are all checkable. That being said, here are my questions:
How/where do I implement the OnPropertyChanged event in the ViewModel when a MenuItem is checked/unchecked?
How can I access model properties of the MenuItem that was checked/unchecked?
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MenuItemsObservableCollection}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="IsCheckable" Value="{Binding IsCheckable}" />
<Setter Property="StaysOpenOnClick" Value="{Binding IsCheckable}" />
<Setter Property="IsChecked" Value="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ViewModel}" ItemsSource="{Binding Path=SubMenuItemsObservableCollection}">
<TextBlock Text="{Binding Header}"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
AddMenuItems();
}
private void AddMenuItems()
{
var subMenu = new ObservableCollection<MenuItemModel>
{
new MenuItemModel { Header = "SubMenu1" },
new MenuItemModel { Header = "SubMenu2"},
new MenuItemModel { Header = "SubMenu3"}
};
MenuItemsObservableCollection = new ObservableCollection<MenuItemModel>
{
new MenuItemModel { Header = "Main Menu", SubMenuItemsObservableCollection = subMenu }
};
}
private void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<MenuItemModel> MenuItemsObservableCollection { get; set; }
}
class MenuItemModel
{
public MenuItemModel()
{
}
public string Header { get; set; }
public string Key { get; set; }
public bool IsCheckable { get; set; } = true
public bool IsChecked { get; set; } = false;
public ObservableCollection<MenuItemModel> SubMenuItemsObservableCollection { get; set; }
}

Related

How to create a selection of bound items in a context menu in wpf?

I'm currently working on a frontend for an emulator in WPF, and am having trouble with this seemingly basic function.
I am trying to create a simple selection of items from within a context menu (in this case of an image). A picture explains it a little better:
I want only one at a time to be selectable/checkable, this data then sets a value in my model which is eventually written to an external file. In a prototype I had that bound to a combobox with two-way binding but that doesn't really make sense with this design.
I tried a couple of different approaches, but did not manage to figure out a proper solution (specifically how to control the isChecked of the non clicked items).
My context menu (xaml) looks like this:
<ContextMenu>
<MenuItem Header="PCSX2 Version" ItemsSource="{Binding Versions}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="StaysOpenOnClick" Value="True" />
<Setter Property="BindingGroup" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}}" />
<EventSetter Event="Click" Handler="SetVersion" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="Config" ItemsSource="{Binding Configs}" />
<MenuItem Header="Create Config" BindingGroup="{Binding}" Click="ShowConfigWizard"/>
</ContextMenu>
And my model is shown below, not much works in the code behind (SetVersion function) yet. As you can see below, I tried a solution involving binding a Tuple but couldn't get that to work (the ischecked value never updated, despite the bound condition being no longer true).
I also tried a radio button based solution (again couldn't get it to work), and just setting checked to false for all items from code behind (was unable to select the sibling menu items from the binding).
public class GameModel : ICloneable
{
public string Game { get; set; }
public string Path { get; set; }
public IEnumerable<string> Versions { get; set; }
public IEnumerable<Tuple<string, bool>> VersionsAndStates => Versions.Select(version => new Tuple<string, bool>(version, Version == version));
public string Version { get; set; }
public IEnumerable<string> Configs { get; set; }
public string Config { get; set; }
public string CoverPath { get; set; }
object ICloneable.Clone() => Clone();
public GameModel Clone() => (GameModel) MemberwiseClone();
}
As is typical I just posted asking about a solution then I figured it out I was so close, just a slight oversight on my part about how the ViewModel works. I'll put my solution below in case anyone else has a similar problem. The main change was in my model.
public class GameModel : ICloneable, INotifyPropertyChanged
{
public string Game { get; set; }
public string Path { get; set; }
public IEnumerable<string> Versions { get; set; }
public IEnumerable<Tuple<string, bool>> VersionsAndStates => Versions.Select(version => new Tuple<string, bool>(version, Version == version));
private string version;
public string Version
{
get => version;
set
{
version = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Version)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(VersionsAndStates)));
}
}
public IEnumerable<string> Configs { get; set; }
public string Config { get; set; }
public string CoverPath { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
object ICloneable.Clone() => Clone();
public GameModel Clone() => (GameModel) MemberwiseClone();
}
and my xaml
<ContextMenu>
<MenuItem Header="PCSX2 Version" ItemsSource="{Binding VersionsAndStates}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Item1}" />
<Setter Property="IsChecked" Value="{Binding Item2, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="StaysOpenOnClick" Value="True" />
<Setter Property="BindingGroup" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}}" />
<EventSetter Event="Click" Handler="SetVersion" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="Config" ItemsSource="{Binding Configs}" />
<MenuItem Header="Create Config" BindingGroup="{Binding}" Click="ShowConfigWizard"/>
</ContextMenu>
And then setting Version on the model to the selected header in my code behind

How to access user control properties from a ResourceDictionary file

Im using DevExpress and Prism in my application which Im new to both. Im trying to access "MyText" in the content item I create in my viewmodel. Currently when this item is created in CreatedSelectionEventReceived() via the viewmodel I don't see this property show up in my parameter list while debugging. I would like to know how to access this once its been created so I can update the property when a property is updated NumGaugeValue via the viewmodel. Right now I can hard code a value for "MyText" but unable to bind to it.
ResourceDictionary.xaml
<ResourceDictionary
x:Class="MyProject.WPF.Resources.CustomShapes"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts"
xmlns:dxdiag="http://schemas.devexpress.com/winfx/2008/xaml/diagram"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxga="http://schemas.devexpress.com/winfx/2008/xaml/gauges"
xmlns:ctrl="clr-namespace:MyProject.WPF.Controls"
xmlns:vm="clr-namespace:MyProject.WPF.ViewModels">
<Style x:Key="NumGaugeContentItem" TargetType="dxdiag:DiagramContentItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ctrl:NumberGauge MyText="22"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MyView.xaml
<dxdiag:DiagramControl
x:Name="Diagram"
Width="{Binding ElementName=Dashboard, Path=ActualWidth}"
Height="{Binding ElementName=Dashboard, Path=ActualHeight}"
AllowAddRemoveItems="True"
AllowMoveItems="True"
AllowResizeItems="True"
CanvasSizeMode="Fill"
GridSize="25,25"
MaxZoomFactor="1"
MinZoomFactor="1"
ScrollMode="Content"
SelectedStencils="BasicShapes"
SelectionMode="Single"
ShowRulers="False"
ShowSelectionRectangle="False"
SnapToGrid="True"
SnapToItems="False"
ZoomFactor="1" />
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<prism:InvokeCommandAction Command="{Binding DiagramControlDropCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type dxdiag:DiagramControl}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
MyViewModel.cs
public class MyViewModel: BindableBase
{
public string NumGaugeValue
{
get { return _numGaugeValue; }
set { SetProperty(ref _numGaugeValue, value); }
}
private DiagramControl DiagramCtrl { get; set; }
private Point CtrlPlacementCoordinates { get; set; }
public DelegateCommand<string> UpdateCommand { get; set; }
public DelegateCommand<DragEventArgs> DiagramControlDropCommand { get; set; }
public MyViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
UpdateCommand = new DelegateCommand<string>(Execute).ObservesProperty(() => NumGaugeValue);
DiagramControlDropCommand = new DelegateCommand<DragEventArgs>(HandleDropOnDxDiagram);
ItemSelectionCommand = new DelegateCommand<object>(ProcessSelectionCommand);
_eventAggregator.GetEvent<ControlSelectionEvent>().Subscribe(ControlSelectionEventReceived);
}
public MyViewModel() { }
private DiagramContentItem CreateDiagramItem(string styleId, Point position)
{
var ctrlItem = new DiagramContentItem() {CustomStyleId = styleId, Position = position};
return ctrlItem;
}
private void HandleDropOnDxDiagram(DragEventArgs sender)
{
DiagramCtrl = (DiagramControl)sender.Source;
CtrlPlacementCoordinates = sender.GetPosition(DiagramCtrl);
}
private void DoDragDrop(object sender)
{
// Grab the control from the accordion item and store as the control that was dropped
var item = (AccordionItem)sender;
var controlDropped = (ControlType)item.Items[0];
if (controlDropped.DataContext != null)
{
DragDrop.DoDragDrop(controlDropped, controlDropped.DataContext, DragDropEffects.Move);
}
}
private void ControlSelectionEventReceived(ControlSelectionMessageInfo controlName)
{
// Create the control that was selected dynamically
switch (controlName.SelectedControlName)
{
case "NumberGauge":
var numGauge = CreateDiagramItem("NumGaugeContentItem", CtrlPlacementCoordinates);
DiagramCtrl.Items.Add(numGauge);
break;
//other cases here but not relevant
}
}
}

WPF TreeView, TwoWay binding for IsExpanded is not affecting GUI from C# code

I am trying to create a TreeView that can display items in a tree hirearchy. I want to be able to use code (C#) to expand and collapse TreeViewItems in the TreeView, through the properties bound to an ObservableCollection.
I have bound a property of my class to IsExpanded, and it seems to work if I set it BEFORE setting the tree's ItemSource - the newly created hierarchy will arrive pre-expanded.
But if I click a button that sets IsExpanded for an item in the collection, it does not expand or collapse the tree items in the GUI.
Here is an ugly screenshot of the program so far. The folders were manually created in an Initialize procedure.
Here is the TreeView xaml in the main window:
<TreeView x:Name="TheProjectTree" Margin="5" BorderBrush="{x:Null}" ItemsSource="{Binding DataSet}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay}" />
<!--<EventSetter Event="Expanded" Handler="TheProjectTreeItem_Expanded" />-->
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding nodes}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=Icon}" Height="16"/>
<TextBlock Text=" " />
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Path=Type}" />
<TextBlock Text=")" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Here is a MyProject class that has the data structures:
using System.Collections.ObjectModel;
namespace Project_X
{
public class MyProject
{
public ObservableCollection<MyNode> nodes;
public MyProject()
{
}
public void Initialize()
{
nodes = new ObservableCollection<MyNode>();
nodes.Add(new MyNode("Untitled Project", "Project"));
AddFolder("0. Initialize");
AddFolder("1. Reset");
AddFolder("2. Migrate");
}
public void AddFolder(string folderName)
{
nodes[0].nodes.Add(new MyProject.MyNode(folderName, "Folder"));
}
public class MyNode
{
public string Name { get; set; }
public string Type { get; set; }
public bool IsExpanded { get; set; }
public ObservableCollection<MyNode> nodes { get; set; }
public MyNode(string theName, string theType)
{
Name = theName;
Type = theType;
nodes = new ObservableCollection<MyNode>();
}
public string Icon
{
get
{
if (Type == "Project")
return "./graphics/icon_projectTree_small.png";
else if (Type == "Folder")
return "./graphics/icon_projectTree_small.png";
else if (Type == "Object")
return "./graphics/icon_projectTree_small.png";
else if (Type == "SQL")
return "./graphics/icon_projectTree_small.png";
else if (Type == "Text")
return "./graphics/icon_projectTree_small.png";
return "./graphics/icon_projectTree_small.png";
}
}
}
}
}
And finally, here is a little test procedure that I can call from a testing button.
private void NewProject()
{
Project = new MyProject(); // fire up the main project variable!
Project.Initialize(); // give it some dummy data!
Project.nodes[0].IsExpanded = true; // pre-expand the top-level project node
TheProjectTree.ItemsSource = Project.nodes; // assign the data set to the tree in the main window
Project.AddFolder("test"); // this works! adding new folders to the collection will show up in the GUI
Project.nodes[0].IsExpanded = true; // this does NOT work! it should collapse the top-levl project node in the tree, but it doesn't
}
I would greatly appreciate it if you could brow-beat some knowledge into me. I usually work in SQL, C# and .NET are not my strong suit. I spent the whole evening trying to wrap my head around MVVM and goodness I now feel like a really crummy programmer!
Implement INotifyPropertyChanged interface to your MyNode Class. Which notifies that the property value has changed.
public class MyNode : INotifyPropertyChanged
{
private bool isExpanded;
public string Name { get; set; }
public string Type { get; set; }
public bool IsExpanded
{
get => isExpanded;
set
{
isExpanded = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsExpanded)));
}
}
public ObservableCollection<MyNode> nodes { get; set; }
public MyNode(string theName, string theType)
{
Name = theName;
Type = theType;
nodes = new ObservableCollection<MyNode>();
}
public event PropertyChangedEventHandler PropertyChanged;
}
Your MyNode class need to implement INotifyPropertyChanged
to let the Gui know that the property has changed.
Then in the setter of the IsExpanded property you will have to call NotifyPropertyChanged has explained in the given link.

Access item in observable collection on item selected

I have a WPF TreeView populated by an observable collection using a hiarchialdatabinding
I need to access the item in my observable collection or the database that was used to populate it.
An example use case is that the user right clicks a treeview item to add a subgroup. I obviously need to access its parent to add the child.
Any suggestions? Im so lost..
I cant just edit the treeview item itself cause then the changes wont reflect back to my database
Database Code:
[Serializable]
public class LoginGroup
{
public string Name { get; set; }
public Guid ID { get; set; }
public List<Login> LoginItems = new List<Login>();
public List<LoginGroup> Children { get; set; }
}
public static ObservableCollection<LoginGroup> _GroupCollection = new ObservableCollection<LoginGroup>();
public ObservableCollection<LoginGroup> GroupCollection
{
get { return _GroupCollection; }
}
TreeView:
<TreeView x:Name="groupView" Width="211" TreeViewItem.Selected="OnTreeItemSelected" DockPanel.Dock="Left" Height="Auto" ItemsSource="{Binding GroupCollection}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
You can just cast the SelectedItem to LoginGroup:
LoginGroup selectedGroup = (LoginGroup)groupView.SelectedItem;
You can't reflect back changed of your properties because they don't have way to "notice" back that they are edited. You need inherit LoginGroup from DependencyObject or implement INotifyPropertyChanged
You should use TreeView's ItemContainer style.
Here's sample TreeNode view model:
public class TreeNode : ViewModel
{
public TreeNode()
{
this.children = new ObservableCollection<TreeNode>();
// the magic goes here
this.addChildCommand = new RelayCommand(obj => AddNewChild());
}
private void AddNewChild()
{
// create new child instance
var child = new TreeNode
{
Name = "This is a new child node.",
IsSelected = true // new child should be selected
};
// add it to collection
children.Add(child);
// expand this node, we want to look at the new child node
IsExpanded = true;
}
public String Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
}
private String name;
public Boolean IsSelected
{
get { return isSelected; }
set
{
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private Boolean isSelected;
public Boolean IsExpanded
{
get { return isExpanded; }
set
{
if (isExpanded != value)
{
isExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
}
private Boolean isExpanded;
public ObservableCollection<TreeNode> Children
{
get { return children; }
}
private ObservableCollection<TreeNode> children;
public ICommand AddChildCommand
{
get { return addChildCommand; }
}
private RelayCommand addChildCommand;
}
Some comments:
ViewModel is any base implementation of INotifyPropertyChanged
interface.
RelayCommand (a.k.a. DelegateCommand) is ICommand implementation for use in MVVM approach.
Here's the view:
<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">
<TreeView ItemsSource="{Binding}">
<TreeView.ItemContainerStyle>
<!-- Let's glue our view models with the view! -->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<!-- Here's menu item, which is responsible for adding new child node -->
<MenuItem Header="Add child..." Command="{Binding AddChildCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Window>
... and sample data context initialization:
public MainWindow()
{
InitializeComponent();
DataContext = new ObservableCollection<TreeNode>
{
new TreeNode { Name = "Root", IsSelected = true }
};
}
Hope this helps.
Upd.
Of course, you have to expose child nodes as the ObservableCollection too. Otherwise, changes made to nodes collection won't be reflected.

Silverlight treeview: save expanded/collapsed state

I'm using a hierarchical tree view in Silverlight 4. This tree can be cleared and rebuilded quite often, depending on the user's actions. When this happends, the tree is collapsed by default, which can be annoying from a user perspective.
So, I want to somehow save which nodes are expanded so I can restore the visual state of my tree after it is clear and reloaded.
My treeview is implemented like this:
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:controls2="clr-namespace:System.Windows;assembly=System.Windows.Controls"
<controls:TreeView x:Name="Tree"
ItemsSource="{Binding Source={StaticResource ViewModel}, Path=TreeStructure, Mode=OneWay}"
ItemTemplate="{StaticResource hierarchicalTemplate}" />
<controls2:HierarchicalDataTemplate x:Key="hierarchicalTemplate" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Value.DisplayName}">
</controls2:HierarchicalDataTemplate>
ItemsSource of my treeview is bound on an ObservableCollection TreeStructure;
Node is a wrapper class that looks like that:
public class Node
{
public object Value { get; private set; }
public ObservableCollection<Node> Children { get; private set; }
public Node(object value)
{
Value = value;
Children = new ObservableCollection<Node>();
}
}
Pretty standard stuff. I saw some solutions for WPF, but I can find anything for the Silverlight tree view...
Any suggestions?
Thanks!
Given the way you are implementing your data as a tree, why not bind the 'TreeViewItem.IsExpanded` dependency property to a bool property on your own Node?
It will need to be an INotifyPropertyChanged property at a minimum so Node will need to implement INotifyPropertyChanged.
In Silverlight 5 you can just set a style like this to bind to the IsExpanded property:
<Style TargetType="sdk:TreeViewItem" x:Key="itemStyle">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
And use with
ItemContainerStyle="{Binding Source={StaticResource itemStyle}}"
In Silverlight 4 there are a number of workarounds.
Here's what I did to bind on the TreeViewItem.IsExpanded property. First, I added an IsExpanded property in my Node class.
public class Node : INotifyPropertyChanged
{
public object Value { get; private set; }
public ObservableCollection<Node> Children { get; private set; }
private bool isExpanded;
public bool IsExpanded
{
get
{
return this.isExpanded;
}
set
{
if (this.isExpanded != value)
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
public Node(object value)
{
Value = value;
Children = new ObservableCollection<Node>();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
After that, I subclassed the TreeView and TreeViewItem controls (I lose the custom theme on my treeview, but whatever...)
public class BindableTreeView : TreeView
{
protected override DependencyObject GetContainerForItemOverride()
{
var itm = new BindableTreeViewItem();
itm.SetBinding(TreeViewItem.IsExpandedProperty, new Binding("IsExpanded") { Mode = BindingMode.TwoWay });
return itm;
}
}
public class BindableTreeViewItem : TreeViewItem
{
protected override DependencyObject GetContainerForItemOverride()
{
var itm = new BindableTreeViewItem();
itm.SetBinding(TreeViewItem.IsExpandedProperty, new Binding("IsExpanded") { Mode = BindingMode.TwoWay });
return itm;
}
}
In my XAML, I just have to use BindableTreeView instead of TreeView, and it works.
The trick is to use SetterValueBindingHelper from here. Then your XAML will look like the following. Make sure you carefully copy what I have below.
<sdk:TreeView.ItemContainerStyle>
<Style TargetType="sdk:TreeViewItem">
<Setter Property="local:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<local:SetterValueBindingHelper>
<local:SetterValueBindingHelper Property="IsSelected" Binding="{Binding Mode=TwoWay, Path=IsSelected}"/>
<local:SetterValueBindingHelper Property="IsExpanded" Binding="{Binding Mode=TwoWay, Path=IsExpanded}"/>
</local:SetterValueBindingHelper>
</Setter.Value>
</Setter>
</Style>
</sdk:TreeView.ItemContainerStyle>
The syntax isn't exactly like what you would use in WPF, but it works and it works well!

Categories

Resources