WPF: create a binding to a nested property in code behind - wpf

I have the following multibinding in my xaml file:
<MyControl:CommandParameter>
<MultiBinding Converter="{commonConverters:MultiBindingConverter}">
<Binding Path="CurrentItem.Id" />
<Binding Path="SelectedItem.Count" />
</MultiBinding>
</Mycontrol:CommandParameter>
How can i define this multibinding in my viewmodel?
Or, when this is not possible, how can I define in my viewmodel that the CanExecute of the command is triggered every time the Id or Count changes?
Another difficulty is that CurrentItem and SelectedItem can be null after initialization and will be initialized while using the application.
Thanks!

To tell WPF your command may have become executable or not executable you can use the ICommand.RaiseCanExecuteChanged event. It'll make WPF call the CanExecute method of your command.
Since you didn't provide your ViewModel's and SelectedItem/CurrentItem's code the following example may not represent your use case but it'll give you the general idea.
Consider having the following custom command class :
public class MyCommand : ICommand
{
public EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
// do some stuff
}
public bool CanExecute(object parameter)
{
// determine if command can be executed
}
public void RaiseCanExecuteChanged()
{
this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
In your ViewModel you could then have something that looks like this
public class MyViewModel
{
private int _id;
public MyCommand SomeCommand { get; set; }
public int Id
{
get => _id;
set
{
// do other stuff (ie: property changed notification)
SomeCommand.RaiseCanExecuteChanged();
}
}
}

Related

WPF Simplifying OnPropertyChanged [duplicate]

I'm pretty new in WPF programming environment. I'm trying to write a program out using MVVM design pattern.
I've did some studies and read up some articles related to it and many of a time I came across this thing called
ViewModelBase
I know what it is.. But may I know specifically where should I begin with to be able to write out my own ViewModelBase? Like... Really understanding what's happening without getting too complicated. Thank you :)
It's worth nothing to use MVVM frameworks if you don't know what's going on inside.
So let's go step by step and build your own ViewModelBase class.
ViewModelBase is class common for all your viewmodels. Let's move all common logic to this class.
Your ViewModels should implement INotifyPropertyChanged (do you understand why?)
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
the [CallerMemberName] attribute is not required, but it will allow you to write:
OnPropertyChanged(); instead of OnPropertyChanged("SomeProperty");, so you will avoid string constant in your code. Example:
public string FirstName
{
set
{
_firstName = value;
OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
}
get{ return _firstName;}
}
Please note, that OnPropertyChanged(() => SomeProperty) is no more recommended, since we have nameof operator in C# 6.
It's common practice to implement properties that calls PropertyChanged like this:
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
Let's define SetProperty in your viewmodelbase:
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
It simply fires PropertyChanged event when value of the property changes and returns true. It does not fire the event when the value has not changed and returns false. The basic idea is, that SetProperty method is virtual and you can extend it in more concrete class, e.g to trigger validation, or by calling PropertyChanging event.
This is pretty it. This is all your ViewModelBase should contain at this stage. The rest depends on your project. For example your app uses page base navigation and you have written your own NavigationService for handling navigation from ViewModel. So you can add NavigationService property to your ViewModelBase class, so you will have access to it from all your viewmodels, if you want.
In order to gain more reusability and keep SRP, I have class called BindableBase which is pretty much the implementation of INotifyPropertyChanged as we have done here. I reuse this class in every WPF/UWP/Silverligt/WindowsPhone solution because it's universal.
Then in each project I create custom ViewModelBase class derived from BindableBase:
public abstract ViewModelBase : BindableBase
{
//project specific logic for all viewmodels.
//E.g in this project I want to use EventAggregator heavily:
public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()
}
if I have app, that uses page based navigation I also specify base class for page viewmodels.
public abstract PageViewModelBase : ViewModelBase
{
//for example all my pages have title:
public string Title {get; private set;}
}
I could have another class for dialogs:
public abstract DialogViewModelBase : ViewModelBase
{
private bool? _dialogResult;
public event EventHandler Closing;
public string Title {get; private set;}
public ObservableCollection<DialogButton> DialogButtons { get; }
public bool? DialogResult
{
get { return _dialogResult; }
set { SetProperty(ref _dialogResult, value); }
}
public void Close()
{
Closing?.Invoke(this, EventArgs.Empty);
}
}
The below class can be used as a ViewModelBase in WPF projects:
public abstract class ViewModelBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches the desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners.This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
// Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
And an example of ViewModel class is:
public class MyViewModel : ViewModelBase
{
private int myProperty;
public int MyProperty
{
get { return myProperty; }
set { SetProperty(ref myProperty, value); }
}
}
For ease of writing, below snippet can be used:
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>OnPropertyChanged</Title>
</Header>
<Snippet>
<SnippetTypes>
<SnippetType>SurroundsWith</SnippetType>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
<Declarations>
<Literal>
<ID>TYPE</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>NAME1</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[private $TYPE$ _$NAME1$;
public $TYPE$ $NAME1$
{
get => _$NAME1$;
set => SetProperty(ref _$NAME1$, value);
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
The full code could be downloaded from here.
You have some nuget package to implement MVVM
MVVM light
MVVM Cross
Prism
For me the easier for a beginner is MVVM light because it provide some code sample.
So the better is to install this nuget package, have a look about the generated code and back to us for more explanations if you need.
In most MVVM frameworks, the base ViewModel classes actually contain very little code - usually just an implementation of INotifyPropertyChanged and some helper functions.
Take a look at the source code for MVVM Light's ViewModelBase and ObservableObject classes. ObservableObject is mostly the INotifyPropertyChanged implementation - using a lambda expression rather than "magic strings" for the property name. ViewModelBase extends ObservableObject and is mostly a utility method to determine if you're running inside the Visual Studio designer
I like this BaseVewModel it gives a nice clean style to your view models. Check out the various 'before' and 'after' comparisons. Of course, nothing is mandatory - if you don't like a feature that the BaseViewModel provides then don't use it. Or modify it because you have the source code. In particular note that there are three different ways to implement properties with change notification - choose the level of sophistication that you understand/feel comfortable with.
To revisit this answer today, I'd like to offer additional productivity improvements when writing MVVM code for Visual Studio.
The Intellisense in Visual Studio can automatically create the SetProperty boilerplate method. To do so, I set the ViewModel in the XAML of my Window (see below). Then, whenever I reference a {Binding Path=NewProperty}, Right Click and Select Quick Actions and Refactoring... (or via Ctrl .). If the SetProperty method isn't made, it will automatically be created for you within your ViewModel class. Further, it'll generate the property and field required for the Binding.
<Window x:Class="My.Project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:My.Project"
mc:Ignorable="d"
xmlns:viewmodel="clr-namespace:My.Project.ViewModels" d:DataContext="{d:DesignInstance Type=viewmodel:MainWindowViewModel}"
Title="MainWindow" Height="360" Width="1000">
...
</Window>
However, this method has drawbacks
The INotifyPropertyChanged is not implemented and the OnPropertyChanged method is not implemented (if you need it)
This would need to be done in every ViewModel
This is specific for Visual Studio
Benefits:
Once the SetProperty method is defined in the project, using the Quick Actions and Refactoring... option will only generate the necessary property and field for you. This also works if you inherit from a ViewModelBase.
Here is the SetProperty method as generated by Visual Studio.
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
// Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
this.OnPropertyChanged(propertyName);
return true;
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace CollectionViewMVVM.ViewModel
{
public class BaseViewModel : INotifyPropertyChanged
{
/*Referencia: https://www.youtube.com/watch?v=PXEt1esjnZ0&ab_channel=Codigo369*/
public INavigation Navigation;
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnpropertyChanged([CallerMemberName] string nombre = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nombre));
}
private ImageSource foto;
public ImageSource Foto
{
get { return foto; }
set
{
foto = value;
OnpropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public async Task DisplayAlert(string title, string message, string cancel)
{
await Application.Current.MainPage.DisplayAlert(title, message, cancel);
}
public async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
{
return await Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if(EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
/*Ejemplo para declarar entrys*/
private string _title;
public string Title
{
get { return _title; }
set
{
SetProperty(ref _title, value);
}
}
/*Para tipos bool*/
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
SetProperty(ref _isBusy, value);
}
}
protected void SetValue<T>(ref T backingFielded, T value, [CallerMemberName] string propertyName = null)
{
if(EqualityComparer<T>.Default.Equals(backingFielded, value))
{
return;
}
backingFielded = value;
OnPropertyChanged(propertyName);
}
}
}

WPF Attach a command to a textbox on return key in NET 3.5

I am trying to attach a command and a commandparameter to a textbox on return key but without success. The parameter is the current text in the same textbox.
<TextBox x:Name="txtSearch">
<TextBox.InputBindings>
<KeyBinding Command="{Binding SearchCommand}"
CommandParameter="{Binding Path=Text, ElementName=txtSearch}" Key="Return" />
</TextBox.InputBindings>
</TextBox>
Basically I want to execute the command when user clicks on return/enter key and pass as a parameter the current text in the textbox.
I have found this link where it is said that in .NET 3.5 command parameter for keybinding is not accepting bindings. So a solution is proposed by code in code-behind but how can I pass a parameter to the command from the code?
First, you'll need to add the KeyBinding to your TextBox and set its Command on code-behind. Just add this in the constructor of your View:
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
KeyBinding kb = new KeyBinding();
kb.Command = (DataContext as MyViewModel).SearchCommand;
kb.Key = Key.Enter;
txtSearch.InputBindings.Add(kb);
}
Then, you can bind the Text property of the TextBox named txtSearch to a property of your ViewModel. This way you don't need to pass a parameter as you can use the value of that property in your ViewModel inside the code that executes your Command.
Your ViewModel should look like this:
public class MyViewModel : ObservableObject
{
private string _txtSearch;
public string TxtSearch
{
get { return _txtSearch; }
set
{
if (value != _txtSearch)
{
_txtSearch = value;
OnPropertyChanged("TxtSearch");
}
}
}
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
{
_searchCommand = new RelayCommand(p => canSearch(), p => search());
}
return _searchCommand;
}
}
private bool canSearch()
{
//implement canExecute logic.
}
private void search()
{
string text = TxtSearch; //here you'll have the string that represents the text of the TextBox txtSearch
//DoSomething
}
}
If you have access to C# 6 (Visual Studio 2015 and later versions), you can alter the call to the OnPropertyChanged to: OnPropertyChanged(nameof(TxtSearch));. This way you get rid of the "magic string" and eventual renaming of the property won't cause any problem for you.
And then your XAML should look like this: (Notice that you need to specify that te UpdateSourceTrigger must be PropertyChanged, so that your TxtSearch property of your ViewModel stays up to date when you hit the Enter key on your TextBox.
<TextBox Text="{Binding TxtSearch, UpdateSourceTrigger=PropertyChanged}" x:Name="txtSearch"/>
Your ViewModel needs to implement INotifyPropertyChanged and you need a proper ICommand implementation. Here I'll use the RelayCommand.
Those implementations are shown below.
Since your framework is .NET 3.5, implement it like this:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This is a implementation of the RelayCommand:
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}

INotifyPropertyChanged vs ObservableCollection for a single value

In the examples for coding with the MVVM pattern and WPF binding, they use INotifyPropertyChanged when it's a single value and ObservableCollection when it is a list of values.
I've also read, you cannot use static variables with INotifyPropertyChanged, but you can with ObservableCollection. I'd like to bind to a static variable.
The easiest (to me at least) workaround, is to use an ObservableCollection and always just use and bind to index 0. Is this appropriate to do? Is there any benefit to using INotifyPropertyChanged instead of an ObservableCollection?
For example:
This seems to be the simplest workaround:
public static ObservableCollection<int> MyValues { get; set; }
<TextBox Text="{Binding MyValue[0]}">
For wanting to do this:
private static int _myValue;
public static int MyValue //does not work
{
get { return _myValue; }
set { _myValue = value; OnPropertyChange(); }
}
<TextBox Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}">
I don't think your analogy is fair(Comparing ObservableCollection with directly with Primitive Type property). Compare ObservableCollection to below class
public class MyClass : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set { text = value;
RaiseChange("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChange(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
}
Now both below will behave same:
public static MyClass Text { get; set; }
public static ObservableCollection<string> Text { get; set; }
If you perform Text.Text = "Hello" for MyClass or Text[0] = "Hello" for ObservableCollection, both will reflected in same way.
Now if you have to use a static property for binding then instead of ObservableCollection I'll advice you to write your new class
cause ObservableCollection has many internal implementations which
probably is of no use to you any actually consuming memory &
perforamnce.
This link may help, it shows the difference between List, ObservableCollection and INotifyPropertyChanged.
Hope this help

Command Binding to Routed Event in WPF User-control

I want to bind Viewmodel command to Usercontrol's Routed Event.
Here is the detailed explanation of what I have.
I have a User Control which have one Image (which shows image) and one Button at bottom (Button to remove Image). I am using a Usercontrol in a ListView.
In my Usercontrol's Code behind I have a RoutedEventHandler to remove the Image:
public event RoutedEventHandler RemoveImage;
In the window where I use this Usercontrol, I have put:
<uc:ucImageListItem x:Name="ImageListItem" RemoveImage="ImageListItem_RemoveImage" />
This code works correctly if My code to remove image is in code behind. but I want to Bind command of Viewmodel to RemoveImage RoutedEvent.
Probably like (not correct)
<uc:ucImageListItem x:Name="ImageListItem" RemoveImage="{binding CommandtoRemove}" />
How to achieve this?
I found something related to RoutedCommand or DependancyProperty, but could not find any proper way, How to use them.
Let me know if I need to further clear my question.
Thanks in anticipation.
Hi this piece of code shows how to call command:
Command handler
public class CommandHandler : ICommand
{
public CommandHandler(Action<object> action,Func<object,bool> canexecute)
{
_action = action;
_canExecute = canexecute;
}
Action<object> _action;
Func<object, bool> _canExecute;
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}
ViewModel
public class MainViewModel
{
private CommandHandler _buttonCommand;
public CommandHandler ButtonCommand
{
get
{
return _buttonCommand ?? (_buttonCommand = new CommandHandler((param) => OnButtonCommand(param),(param)=>true));
}
}
private void OnButtonCommand(object obj)
{
//DO things here whatever you want to do on Button click
}
}
View
<Button Command="{Binding ButtonCommand}" Content="ok"/>
you need to pass two parameters to CommandHandler Constructor one is Action that you want to fire on Command and second param is func that must return bool. If func evaluates to true only then the Action of Command is fired.And the param in action and func is what you will bind to the CommandParameter in my case above it will be null as I havent binded the CommandParameter.I hope this will help.

WPF ViewModel Commands CanExecute issue

I'm having some difficulty with Context Menu commands on my View Model.
I'm implementing the ICommand interface for each command within the View Model, then creating a ContextMenu within the resources of the View (MainWindow), and using a CommandReference from the MVVMToolkit to access the current DataContext (ViewModel) Commands.
When I debug the application, it appears that the CanExecute method on the command is not being called except at the creation of the window, therefore my Context MenuItems are not being enabled or disabled as I would have expected.
I've cooked up a simple sample (attached here) which is indicative of my actual application and summarised below. Any help would be greatly appreciated!
This is the ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
The DisplayValueCommand is such:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
And finally, the view is defined in Xaml:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
To complete Will's answer, here's a "standard" implementation of the CanExecuteChanged event :
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(from Josh Smith's RelayCommand class)
By the way, you should probably consider using RelayCommand or DelegateCommand : you'll quickly get tired of creating new command classes for each and every command of you ViewModels...
You have to keep track of when the status of CanExecute has changed and fire the ICommand.CanExecuteChanged event.
Also, you might find that it doesn't always work, and in these cases a call to CommandManager.InvalidateRequerySuggested() is required to kick the command manager in the ass.
If you find that this takes too long, check out the answer to this question.
Thank you for the speedy replies. This approach does work if you are binding the commands to a standard Button in the Window (which has access to the View Model via its DataContext), for example; CanExecute is shown to be called quite frequently when using the CommandManager as you suggest on ICommand implementing classes or by using RelayCommand and DelegateCommand.
However, binding the same commands via a CommandReference in the ContextMenu
do not act in the same way.
In order for the same behaviour, I must also include the EventHandler from Josh Smith's RelayCommand, within CommandReference, but in doing so I must comment out some code from within the OnCommandChanged Method. I'm not entirely sure why it is there, perhaps it is preventing event memory leaks (at a guess!)?
public class CommandReference : Freezable, ICommand
{
public CommandReference()
{
// Blank
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
//if (oldCommand != null)
//{
// oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
//}
//if (newCommand != null)
//{
// newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
//}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}
However, binding the same commands via a CommandReference in the
ContextMenu do not act in the same way.
That's a bug in CommandReference implementation. It follows from these two points:
It is recommended that the implementers of ICommand.CanExecuteChanged hold only weak references to the handlers (see this answer).
Consumers of ICommand.CanExecuteChanged should expect (1) and hence should hold strong references to the handlers they register with ICommand.CanExecuteChanged
The common implementations of RelayCommand and DelegateCommand abide by (1). The CommandReference implementation doesn't abide by (2) when it subscribes to newCommand.CanExecuteChanged. So the handler object is collected and after that CommandReference no longer gets any notifications that it was counting on.
The fix is to hold a strong ref to the handler in CommandReference:
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
In order for the same behaviour, I must also include the EventHandler
from Josh Smith's RelayCommand, within CommandReference, but in doing
so I must comment out some code from within the OnCommandChanged
Method. I'm not entirely sure why it is there, perhaps it is
preventing event memory leaks (at a guess!)?
Note that your approach of forwarding subscription to CommandManager.RequerySuggested also eliminates the bug (there's no more unreferenced handler to begin with), but it handicaps the CommandReference functionality. The command with which CommandReference is associated is free to raise CanExecuteChanged directly (instead of relying on CommandManager to issue a requery request), but this event would be swallowed and never reach the command source bound to the CommandReference. This should also answer your question as to why CommandReference is implemented by subscribing to newCommand.CanExecuteChanged.
UPDATE: submitted an issue on CodePlex
An easier solution for me, was to set the CommandTarget on the MenuItem.
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
More info: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

Resources