How does NotifyOnValidationError end up calling CanExecute (and why doesn't it work with MVVM Light RelayCommand) - wpf

Maybe the longest question title of all time! Because this is a two part question.
(1) I do not understand how setting NotifyOnValidationError="True" can trigger updates to my CanExecute. There is a bit of magic involved here that I need to understand. Someone(thing) subscribes to the CanExecuteChanged event of my ICommand but the call stack points to External code, so I can not figure out what is going on.
(2) Maybe the most important follow up questions is: Why does it not work in MVVM Light RelayCommand! The CanExecute is only called once at initialization and then never again. Looking at the source code for RelayCommand in MVVM Light does not reveal any chocking differences compared to my own implementation. I should mention that Prism's DelegateCommand does not seem to work either.
(Bonus) Maybee I am approaching this problem the wrong way? I just basically want to enable/disable buttons based on Validation failiures.
XAML (snippet):
<TextBox Grid.Column="1" Grid.Row="0">
<Binding Path="X" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox>
<Button Grid.Column="1" Grid.Row="3" Command="{Binding CalculateCommand}">
Calculate
</Button>
RelayCommand:
public class MyRelayCommand : ICommand
{
readonly Action<object> Execute_;
readonly Predicate<object> CanExecute_;
public MyRelayCommand(Action<object> Execute, Predicate<object> CanExecute)
{
if (Execute == null)
throw new ArgumentNullException("No action to execute for this command.");
Execute_ = Execute;
CanExecute_ = CanExecute;
}
public bool CanExecute(object parameter)
{
return (CanExecute_ == null) ? true : CanExecute_(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
Execute_(parameter);
}
}
ViewModel:
private DelegateCommand _calculateCommmand;
public DelegateCommand CalculateCommand
{
get
{
return _calculateCommmand ?? (_calculateCommmand = new DelegateCommand(
() =>
{
Sum = X + X;
},
() =>
{
try
{
Convert.ChangeType(X, TypeCode.Byte);
return true;
}
catch
{
return false;
}
}));
}
}
PS: If you wanna buy my X + X program when it is done email me at sales#xplusx.com

(2) I figured this one out myself. You can choose to include RelayCommand from two different namespaces, make sure you use
using GalaSoft.MvvmLight.CommandWpf;
I am still looking for a good answer to (1), how the plumbing works that raises CanExecutetChanged based on validation error.

I don't think it depends on the ICommand implementation. In yours, I see a public event EventHandler CanExecuteChanged, where you tell the CommandManager to handle the invocation of your command's CanExecute() method. Without the CommandManager, you would have to handle this yourself, e.g. by providing your ICommand implementation with a public void RaiseCanExecuteChanged() method that your ViewModel has to call for every command it considers to be needed to recalculated, e.g. inside the ViewModel's OnPropertyChanged. Example: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right
So the CommandManager does the magic for you. As soon as you invoke your ViewModel's PropertyChanged event, the "external code" handles the affected commands and asks them for a fresh CanExecute() value.

(1) I think it goes like this.
When we bind a RelayCommand : ICommand to Button.Command, the binding process will also attach an eventhandler to the ICommand.CanExecuteChanged. This is default behavior.
The eventhandler passed to CanExecutedChanged will be passed along and attached to the static event CommandManager.RequerySuggested.
When a validation error occurs and the NotifyOnValidationError is set an external force, or a jedi, will raise the RequerySuggested event which will broadcast to ALL active commands.
The Button recieves the event and consequently calls CanExecute to know if it shold disable/enable the button.
I would like to know more about the third bullet point above, so I will keep the question open for a little longer to give the experts a chance to chime in.

Related

How to bind command in ViewModel to a command in Behavior?

WPF project + Prism 7 + (Pure MVVM pattern)
Simple, I have TextBox which need to be cleared when some button is pressed (without the violation to the MVVM pattern)
<Button Command="{Binding ClearCommand}"/>
<TextBox Text="{Binding File}">
<i:Interaction.Behaviors>
<local:ClearTextBehavior ClearTextCommand="{Binding ClearCommand, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</TextBox>
ViewModel
public class ViewModel {
public ICommand ClearCommand { get; set; }
}
Behavior
public class ClearTextBehavior : Behavior<TextBox>
{
public ICommand ClearTextCommand
{
get { return (ICommand)GetValue(ClearTextCommandProperty); }
set
{
SetValue(ClearTextCommandProperty, value);
RaisePropertyChanged();
}
}
public static readonly DependencyProperty ClearTextCommandProperty =
DependencyProperty.Register(nameof(ClearTextCommand), typeof(ICommand), typeof(ClearTextBehavior));
public ClearTextBehavior()
{
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
}
private void ClearTextCommandExecuted()
{
this.AssociatedObject.Clear();
}
}
The problem is the command in the ViewModel is always null (it did not bound to the command in the Behavior), Although I made sure that it is initialized in the behavior class.
NOTE: please do NOT suggest to set the File property to empty string, because this is just an example, In my real case, I need to select all the Text, so I really need an access to the AssociatedObject of the behavior
If i understood your Question correctly, you want to know why the ICommand in the ViewModel is not set to the DelegateCommand defined in the Behaviour.
The Problem is, that the ICommand and the DelegateCommand do not have a direct connection. I assume you may misunderstood how a Binding works and what happens by using those.
First of all, the ICommand is 'comes' from a Class and is therefore a reference Type.
Second, the reference to the ICommand is saved within the DependencyProperty ClearTextCommandProperty.
Third, by using a Binding in the XAML something like this happens as C# code:
Binding binding = new Binding();
binding.Path = new PropertyPath("ClearTextCommand");
binding.Source = ClearCommand;
BindingOperations.SetBinding(TextBox.ClearTextCommandProperty, binding);
Now the important thing: I don't know exactly which assignment comes first, but both lines will override the Value reference in the ClearTextCommandProperty!
//either here
SetBinding(TextBox.ClearTextCommandProperty, binding);
//or here
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
//Which could be written as
SetValue(ClearTextCommandProperty, new DelegateCommand(ClearTextCommandExecuted));
At no point there is an assignment like this:
ViewModel.ClearCommand = SomeICommand;
Therefore it is Null, as #Andy mentioned
Edited to match select all Text
Additionally, i suggest you drop this complex stuff and use the full potential of the Interactivity Package like this:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<utils:SelectAllText TargetName="TextToSelect"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<TextBox x:Name="TextToSelect" Text="{Binding File}"/>
And the SelectAllText
public class SelectAllText : TargetedTriggerAction<TextBox>
{
protected override void Invoke(object parameter)
{
if (Target == null) return;
Target.SelectAll();
Target.Focus();
}
}
If you take a look at this sample here:
https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx
You will notice that I have an ICommand there and it's set up to run a method.
If it was just an ICommand with a Get and Set like you have there then it would be NULL. There's a property but it's null until it is set to something.
This a very clunky way to implement an ICommand but relies on no external libraries or anything.
If you take a look at the second article in that series, it uses mvvmlight and relaycommand so creating a command is rather less clunky.
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
public RelayCommand AddListCommand { get; set; }
public MainWindowViewModel()
{
AddListCommand = new RelayCommand(AddList);
}
private void AddList()
{
stringList.Add(myString));
}
If you look at that code AddListCommand is initially null.
It is set in the constructor to a new RelayCommand which means it is then not null.
This is fairly simple but the code for the command is in a different place to the property so a more elegant approach is usual. As shown here: https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx
Having said all that.
Selecting all text is something to do in the view, not the viewmodel.
You shouldn't really be passing a piece of UI from the view into a viewmodel.
Rather than a command it could well be that you should be binding a bool which is set in the viewmodel and acted on in the behaviour.

ICommand doesn't update the IsEnabled on a button using CanExecute

I have a very simple button binded to a command
<Button Content="Add" Margin="10,10,10,0" Command="{Binding SaveCommand}" ></Button>
My command code
public ICommand SaveCommand
{
get;
internal set;
}
private bool CanExecuteSaveCommand()
{
return DateTime.Now.Second % 2 == 0;
}
private void CreateSaveCommand()
{
SaveCommand = new DelegateCommand(param => this.SaveExecute(), param => CanExecuteSaveCommand());
}
public void SaveExecute()
{
PharmacyItem newItem = new PharmacyItem();
newItem.Name = ItemToAdd.Name;
newItem.IsleNumber = ItemToAdd.IsleNumber;
newItem.ExpDate = ItemToAdd.ExpDate;
PI.Add(newItem);
}
The code effectively blocks the command from running based on CanExecuteSaveCommand but the button is never disabled, is there a way to achieve this?
ICommand.CanExecute() is called automatically by WPF whenever it thinks the command availability may have changed. This generally tends to be on user activity, eg keyboard events, focus events etc.
Since your command availability changes purely based on time, WPF has no way of guessing that it has changed. Instead you need to give it a hint by calling CommandManager.InvalidateRequerySuggested();
Since your command availability changes every second, you would need to set up a timer to call this function at least every second.
Note that although InvalidateRequerySuggested() is the easiest solution, it will cause WPF to re-evaluate ALL command availabilities. If this is a performance problem, you can raise the CanExecuteChanged event on your ICommand instance instead.

Double Click on a WPF ListView - how to fire a command and not use an event handler

I am a little confused on how to implement an event as a command in my particular situation. I want to honour MVVM, but don't get how in this case.
I have a WPF 'view' - viewCustomerSearch. This has some text boxes on it, and when the user clicks 'Search' the results are populated in ListView. viewCustomerSearch is bound to viewmodelCustomerSearch, and it works great.
viewCustomerSearch is hosted on viewCustomer.
I want to know have viewCustomerSearch expose a custom command - CustomerSelectedCommand - that is 'fired' whenever the ListView in viesCustomerSearch is double clicked, and then handled by the viewmodel behind viewCustomer (which is viewmodelCustomer). This seems the theoretical MVVM pattern implemented correctly.
I have broken down the main problem into three smaller problems, but hopefully you can see they are all components of the same challenge.
FIRST PROBLEM: in order to have viewCustomerSearch expose a custom command I seem to have to put this code in viewCustomerSearch - which seems to 'break' MVVM (no code in the view code behind).
public readonly DependencyProperty CustomerSelectedCommandProperty = DependencyProperty.Register("CustomerSelectedCommand", typeof(ICommand), typeof(viewCustomerSearch));
public ICommand CustomerSelectedCommand
{
get { return (ICommand)GetValue(CustomerSelectedCommandProperty); }
set { SetValue(CustomerSelectedCommandProperty, value); }
}
SECOND PROBLEM (and this is the one that is really getting to me): Best explained by showing what I would do which breaks MVVM. I would have an event handler in the view:
private void lstResults_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (CustomerSelectedCommand != null) CustomerSelectedCommand.Execute(((ViewModels.viewmodelCustomerSearchResult)this.lstResults.SelectedItem).CustomerId);
}
Well ... I know that you shouldn't put this event handler here; rather it should have a Command to handle it in the viewmodelCustomerSearch. The two problems here are
because the 'CustomerSelectedCommand' ICommand is implemented in
viewCustomerSearch, viewmodelCustomerSearch can't see it to fire it.
I cannot see how to bind the MouseDoubleClick event to a command, instead of an event handler in the view code behind. I am reading about Attached Properties, but cannot see how they are to be applied here.
(Please note: I am using the common 'RelayCommand' elsewhere in the application; does this come into play here??)
THIRD PROBLEM: When I do use the non-MVVM way of firing the command in the code behind event handler, you can see that I am passing in the Selected Customer Id as an arguement into the command. How do I see that argument in the Command handler in viewCustomer? I create a new RelayCommand to handle it, but it seems the Execute method does not take arguments?
Given all of the above, I have to say that I do NOT personally subscribe to the 'MVVM means NO CODE IN THE VIEW'. That seems crazy to me; code that is entirely to do with the view, and the view only, should not - IMHO - go in the viewmodel. That said, though, this does seem like logic-y stuff (not view stuff).
Many thanks for some insight. Sorry for the long post; trying to balance enough information for you to help me with 'War and Peace'.
DS
In your view you can add a "Command" property in xaml and bind it to your ViewModel's command
Command="{Binding CustomerSelectedCommand}"
Parameters can be passed in multiple ways. Most of the time, I just have other items bound to my ViewModel and I can just use them directly. However there is also a property called CommandParameter, here's an example of specifying it in XAML.
CommandParameter="{Binding ElementName=txtPassword}"
then in my ViewModel the definition of my Command looks like this
private void UserLogonCommandExecute(object parameter)
{
...
var password_box = parameter as PasswordBox;
...
}
It sounds like you already know how to set up a RelayCommand in your ViewModel so I won't go into that. I found How Do I: Build Data-driven WPF Application using the MVVM pattern helpful when I was getting started.
Per Comment Request Command Property Example
I'm just going to grab some working code, here's how you add a Command property to a button in XAML.
<Button Command="{Binding ConnectCommand}">
//Your button content and closing </Button> here
This assume you have set your DataContext to a ViewModel that has a Command called ConnectCommand. Here's an example for ConnectCommand. You'll need to replace the contents of ConnectCommandCanExecute and ConnectCommandExecute with whatever work you want done.
public ICommand ConnectCommand
{
get
{
if (_connectCommand == null)
{
_connectCommand = new RelayCommand(param => ConnectCommandExecute(),
param => ConnectCommandCanExecute);
}
return _connectCommand;
}
}
private bool ConnectCommandCanExecute
{
get { return !_instrumentModel.IsConnected; }
}
private void ConnectCommandExecute()
{
if (TcpSettingsChanged()) SaveTcpSettings();
_instrumentModel.Connect(_tcpData);
}
RelayClass
One part of making this simple is the RelayClass I have in one of my core library .dlls. I probably got this from one of the videos I watched. This can be cut and pasted in it's entirety, there is nothing here you need to customize, except you'll probably want to change the namespace this is in.
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace Syncor.MvvmLib
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public RelayCommand(Action<object> execute)
: this(execute, (Predicate<object>) null)
{
this._execute = execute;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
this._execute = execute;
this._canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
if (this._canExecute != null)
return this._canExecute(parameter);
else
return true;
}
public void Execute(object parameter)
{
this._execute(parameter);
}
}
}
Why don't you name it "DoubleClickCommand" that way you don't put business logic in your control. And then bind this command to your viewmodel, Like Tod explained.
Regarding your code behind, there is a pure xaml solution, to be more precise it involves attached behaviors, but does not need to override a WPF class(which i like to avoid), search for "fire command on event" for example this.
One final thing: Code Behind does NOT break MVVM in any way, i wonder where this myth came from. Code behind is perfectly fine! MVVM is to separate view and logic, not telling you where to put your code. Design principles should help, not hinder you.

Data Validation in Silverlight 4

I have control in SL4. I want data validation on button click. Big problem is normally SL4 give validation using binding property.
like example given shown in this example
http://weblogs.asp.net/dwahlin/archive/2010/08/15/validating-data-in-silverlight-4-applications-idataerrorinfo.aspx
<TextBox Text="{Binding Name,Mode=TwoWay,ValidatesOnDataErrors=true}"
Height="23"
Width="120"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
BUT I WANT TO SHOW ERROR MESSAGE LIKE THIS ....
using my own code like on button click i check
(textbox1.text == null ) then set this style of error to textbox1
One way of deferring validation is to set the property UpdateSourceTrigger=Explicit in the bindings. If you do this, the bindings won't update the source objects, and hence won't cause validation errors, until you explicitly tell the bindings to do so. When your button is clicked, you force an update on the bindings, using a line such as the following for each control:
someTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
You then have your property setters throwing exceptions for invalid data.
This approach can be a bit of a pain if there are quite a lot of controls to force binding updates on.
Also, forcing an update on the bindings has to be done in the code-behind of a control. If you're using a Command with the button as well then you might run in to an issue. Buttons can have both a Command and a Click event handler, and both will execute when the button is clicked on, but I don't know the order in which this happens or even if an order can be guaranteed. A quick experiment suggested that the event handler was executed before the command, but I don't know whether this is undefined behaviour. There is therefore the chance that the command will be fired before the bindings have been updated.
An approach to programmaticaly creating validation tooltips is to bind another property of the textbox and then deliberately cause an error with this binding.
'sapient' posted a complete solution, including code on the Silverlight forums (search for the post dated 07-08-2009 4:56 PM). In short, he/she creates a helper object with a property whose getter throws an exception, binds the Tag property of the textbox to this helper object and then forces an update on the binding.
'sapient's code was written before Silverlight 4 was released. We'll 'upgrade' his/her code to Silverlight 4. The class ControlValidationHelper becomes the following:
public class ControlValidationHelper : IDataErrorInfo
{
public string Message { get; set; }
public object ValidationError { get; set; }
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string columnName]
{
get { return Message; }
}
}
It's easy enough to knock up a quick demo application to try this out. I created the following three controls:
<TextBox x:Name="tbx" Text="{Binding Path=Text, ValidatesOnDataErrors=True, NotifyOnValidationError=True, Mode=TwoWay}" />
<Button Click="ForceError_Click">Force error</Button>
<Button Click="ClearError_Click">Clear error</Button>
The Text property and the event handlers for the two buttons live in the code-behind and are as follows:
public string Text { get; set; }
private void ForceError_Click(object sender, RoutedEventArgs e)
{
var helper = new ControlValidationHelper() { Message = "oh no!" };
tbx.SetBinding(Control.TagProperty, new Binding("ValidationError")
{
Mode = BindingMode.TwoWay,
NotifyOnValidationError = true,
ValidatesOnDataErrors = true,
UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
Source = helper
});
tbx.GetBindingExpression(Control.TagProperty).UpdateSource();
}
private void ClearError_Click(object sender, RoutedEventArgs e)
{
BindingExpression b = tbx.GetBindingExpression(Control.TagProperty);
if (b != null)
{
((ControlValidationHelper)b.DataItem).Message = null;
b.UpdateSource();
}
}
The 'Force error' button should make a validation error appear on the textbox, and the 'Clear error' button should make it go away.
One potential downside of this approach occurs if you are using a ValidationSummary. The ValidationSummary will list all validation errors against ValidationError instead of against the name of each property.
Although my answer wasn't regarded as preferable, I'm still sure that the MVVM pattern is the best choice to perform validation.
In my code you should use the model validator from this post about validation and any mvvm framework, for example MVVM Light.
It is much easier to add validation rules using the view model and model validator classes:
public class PersonViewModel : ViewModelBase, INotifyDataErrorInfo
{
private ModelValidator _validator = new ModelValidator();
public PersonViewModel()
{
this._validator.AddValidationFor(() => this.Age)
.Must(() => this.Age > 0)
.Show("Age must be greater than zero");
}
}
And you can validate the model if and only if a user explicitly clicks a button:
#region INotifyDataErrorInfo
public IEnumerable GetErrors(string propertyName)
{
return this._validator.GetErrors(propertyName);
}
public bool HasErrors
{
get { return this._validator.ErrorMessages.Count > 0; }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { };
protected void OnErrorsChanged(string propertyName)
{
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
this.RaisePropertyChanged("HasErrors");
}
#endregion
public bool Validate()
{
var result = this._validator.ValidateAll();
this._validator.PropertyNames.ForEach(OnErrorsChanged);
return result;
}
As everyone can see, there is nothing difficult here, just 20-30 lines of code.
Moreover, the MVVM approach is much more flexible and you can reuse some common validation scenaries among several view models.

WPF CommandParameter is NULL first time CanExecute is called

I have run into an issue with WPF and Commands that are bound to a Button inside the DataTemplate of an ItemsControl. The scenario is quite straight forward. The ItemsControl is bound to a list of objects, and I want to be able to remove each object in the list by clicking a Button. The Button executes a Command, and the Command takes care of the deletion. The CommandParameter is bound to the Object I want to delete. That way I know what the user clicked. A user should only be able to delete their "own" objects - so I need to do some checks in the "CanExecute" call of the Command to verify that the user has the right permissions.
The problem is that the parameter passed to CanExecute is NULL the first time it's called - so I can't run the logic to enable/disable the command. However, if I make it allways enabled, and then click the button to execute the command, the CommandParameter is passed in correctly. So that means that the binding against the CommandParameter is working.
The XAML for the ItemsControl and the DataTemplate looks like this:
<ItemsControl
x:Name="commentsList"
ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
Width="Auto" Height="Auto">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button
Content="Delete"
FontSize="10"
Command="{Binding Path=DataContext.DeleteCommentCommand, ElementName=commentsList}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
So as you can see I have a list of Comments objects. I want the CommandParameter of the DeleteCommentCommand to be bound to the Command object.
So I guess my question is: have anyone experienced this problem before? CanExecute gets called on my Command, but the parameter is always NULL the first time - why is that?
Update: I was able to narrow the problem down a little. I added an empty Debug ValueConverter so that I could output a message when the CommandParameter is data bound. Turns out the problem is that the CanExecute method is executed before the CommandParameter is bound to the button. I have tried to set the CommandParameter before the Command (like suggested) - but it still doesn't work. Any tips on how to control it.
Update2: Is there any way to detect when the binding is "done", so that I can force re-evaluation of the command? Also - is it a problem that I have multiple Buttons (one for each item in the ItemsControl) that bind to the same instance of a Command-object?
Update3: I have uploaded a reproduction of the bug to my SkyDrive: http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip
I was having this same issue while trying to bind to a command on my view model.
I changed it to use a relative source binding rather than referring to the element by name and that did the trick. Parameter binding didn't change.
Old Code:
Command="{Binding DataContext.MyCommand, ElementName=myWindow}"
New Code:
Command="{Binding DataContext.MyCommand, RelativeSource={RelativeSource AncestorType=Views:MyView}}"
Update: I just came across this issue without using ElementName, I'm binding to a command on my view model and my data context of the button is my view model. In this case I had to simply move the CommandParameter attribute before the Command attribute in the Button declaration (in XAML).
CommandParameter="{Binding Groups}"
Command="{Binding StartCommand}"
I have found that the order in which I set Command and CommandParameter makes a difference. Setting the Command property causes CanExecute to be called immediately, so you want CommandParameter to already be set at that point.
I have found that switching the order of the properties in the XAML can actually have an effect, though I'm not confident that it will solve your problem. It's worth a try, though.
You seem to be suggesting that the button never becomes enabled, which is surprising, since I would expect the CommandParameter to be set shortly after the Command property in your example. Does calling CommandManager.InvalidateRequerySuggested() cause the button to become enabled?
I stumbled upon a similar problem and solved it using my trusty TriggerConverter.
public class TriggerConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// First value is target value.
// All others are update triggers only.
if (values.Length < 1) return Binding.DoNothing;
return values[0];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
This value converter takes any number of parameters and passes the first of them back as the converted value. When used in a MultiBinding in your case it looks like the following.
<ItemsControl
x:Name="commentsList"
ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
Width="Auto" Height="Auto">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button
Content="Delete"
FontSize="10"
CommandParameter="{Binding}">
<Button.Command>
<MultiBinding Converter="{StaticResource TriggerConverter}">
<Binding Path="DataContext.DeleteCommentCommand"
ElementName="commentsList" />
<Binding />
</MultiBinding>
</Button.Command>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You will have to add TriggerConverter as a resource somewhere for this to work. Now the Command property is set not before the value for the CommandParameter has become available. You could even bind to RelativeSource.Self and CommandParameter instead of . to achieve the same effect.
I've come up with another option to work around this issue that I wanted to share. Because the CanExecute method of the command gets executed before the CommandParameter property is set, I created a helper class with an attached property that forces the CanExecute method to be called again when the binding changes.
public static class ButtonHelper
{
public static DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
"CommandParameter",
typeof(object),
typeof(ButtonHelper),
new PropertyMetadata(CommandParameter_Changed));
private static void CommandParameter_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as ButtonBase;
if (target == null)
return;
target.CommandParameter = e.NewValue;
var temp = target.Command;
// Have to set it to null first or CanExecute won't be called.
target.Command = null;
target.Command = temp;
}
public static object GetCommandParameter(ButtonBase target)
{
return target.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(ButtonBase target, object value)
{
target.SetValue(CommandParameterProperty, value);
}
}
And then on the button you want to bind a command parameter to...
<Button
Content="Press Me"
Command="{Binding}"
helpers:ButtonHelper.CommandParameter="{Binding MyParameter}" />
I hope this perhaps helps someone else with the issue.
I'll add what worked for me for a DataGridTemplateColumn with a button.
Change the binding from:
CommandParameter="{Binding .}"
to
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"
Not sure why it works, but it did for me.
I recently came across the same problem (for me it was for the menu items in a context menu), nad while it may not be a suitable solution for every situation, I found a different (and a lot shorter!) way of solving this problem:
<MenuItem Header="Open file" Command="{Binding Tag.CommandOpenFile, IsAsync=True, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" CommandParameter="{Binding Name}" />
Ignoring the Tag-based workaround for the special case of context menu, the key here is to bind the CommandParameter regularly, but bind the Command with the additional IsAsync=True. This will delay the binding of the actual command (and therefore its CanExecute call) a bit, so the parameter will already be available. This means, though, that for a brief moment, the enabled-state might be wrong, but for my case, that was perfectly acceptable.
You may be able to use my CommandParameterBehavior that I posted to the Prism forums yesterday. It adds the missing behaviour where a change to the CommandParameter cause the Command to be re-queried.
There's some complexity here caused by my attempts to avoid the memory leak caused if you call PropertyDescriptor.AddValueChanged without later calling PropertyDescriptor.RemoveValueChanged. I try and fix that by unregistering the handler when the ekement is unloaded.
You'll probably need to remove the IDelegateCommand stuff unless you're using Prism (and want to make the same changes as me to the Prism library). Also note that we don't generally use RoutedCommands here (we use Prism's DelegateCommand<T> for pretty much everything) so please don't hold me responsible if my call to CommandManager.InvalidateRequerySuggested sets off some sort of quantum wavefuntion collapse cascade that destroys the known universe or anything.
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace Microsoft.Practices.Composite.Wpf.Commands
{
/// <summary>
/// This class provides an attached property that, when set to true, will cause changes to the element's CommandParameter to
/// trigger the CanExecute handler to be called on the Command.
/// </summary>
public static class CommandParameterBehavior
{
/// <summary>
/// Identifies the IsCommandRequeriedOnChange attached property
/// </summary>
/// <remarks>
/// When a control has the <see cref="IsCommandRequeriedOnChangeProperty" />
/// attached property set to true, then any change to it's
/// <see cref="System.Windows.Controls.Primitives.ButtonBase.CommandParameter" /> property will cause the state of
/// the command attached to it's <see cref="System.Windows.Controls.Primitives.ButtonBase.Command" /> property to
/// be reevaluated.
/// </remarks>
public static readonly DependencyProperty IsCommandRequeriedOnChangeProperty =
DependencyProperty.RegisterAttached("IsCommandRequeriedOnChange",
typeof(bool),
typeof(CommandParameterBehavior),
new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCommandRequeriedOnChangeChanged)));
/// <summary>
/// Gets the value for the <see cref="IsCommandRequeriedOnChangeProperty"/> attached property.
/// </summary>
/// <param name="target">The object to adapt.</param>
/// <returns>Whether the update on change behavior is enabled.</returns>
public static bool GetIsCommandRequeriedOnChange(DependencyObject target)
{
return (bool)target.GetValue(IsCommandRequeriedOnChangeProperty);
}
/// <summary>
/// Sets the <see cref="IsCommandRequeriedOnChangeProperty"/> attached property.
/// </summary>
/// <param name="target">The object to adapt. This is typically a <see cref="System.Windows.Controls.Primitives.ButtonBase" />,
/// <see cref="System.Windows.Controls.MenuItem" /> or <see cref="System.Windows.Documents.Hyperlink" /></param>
/// <param name="value">Whether the update behaviour should be enabled.</param>
public static void SetIsCommandRequeriedOnChange(DependencyObject target, bool value)
{
target.SetValue(IsCommandRequeriedOnChangeProperty, value);
}
private static void OnIsCommandRequeriedOnChangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ICommandSource))
return;
if (!(d is FrameworkElement || d is FrameworkContentElement))
return;
if ((bool)e.NewValue)
{
HookCommandParameterChanged(d);
}
else
{
UnhookCommandParameterChanged(d);
}
UpdateCommandState(d);
}
private static PropertyDescriptor GetCommandParameterPropertyDescriptor(object source)
{
return TypeDescriptor.GetProperties(source.GetType())["CommandParameter"];
}
private static void HookCommandParameterChanged(object source)
{
var propertyDescriptor = GetCommandParameterPropertyDescriptor(source);
propertyDescriptor.AddValueChanged(source, OnCommandParameterChanged);
// N.B. Using PropertyDescriptor.AddValueChanged will cause "source" to never be garbage collected,
// so we need to hook the Unloaded event and call RemoveValueChanged there.
HookUnloaded(source);
}
private static void UnhookCommandParameterChanged(object source)
{
var propertyDescriptor = GetCommandParameterPropertyDescriptor(source);
propertyDescriptor.RemoveValueChanged(source, OnCommandParameterChanged);
UnhookUnloaded(source);
}
private static void HookUnloaded(object source)
{
var fe = source as FrameworkElement;
if (fe != null)
{
fe.Unloaded += OnUnloaded;
}
var fce = source as FrameworkContentElement;
if (fce != null)
{
fce.Unloaded += OnUnloaded;
}
}
private static void UnhookUnloaded(object source)
{
var fe = source as FrameworkElement;
if (fe != null)
{
fe.Unloaded -= OnUnloaded;
}
var fce = source as FrameworkContentElement;
if (fce != null)
{
fce.Unloaded -= OnUnloaded;
}
}
static void OnUnloaded(object sender, RoutedEventArgs e)
{
UnhookCommandParameterChanged(sender);
}
static void OnCommandParameterChanged(object sender, EventArgs ea)
{
UpdateCommandState(sender);
}
private static void UpdateCommandState(object target)
{
var commandSource = target as ICommandSource;
if (commandSource == null)
return;
var rc = commandSource.Command as RoutedCommand;
if (rc != null)
{
CommandManager.InvalidateRequerySuggested();
}
var dc = commandSource.Command as IDelegateCommand;
if (dc != null)
{
dc.RaiseCanExecuteChanged();
}
}
}
}
There's a relatively simple way to "fix" this problem with DelegateCommand, though it requires updating the DelegateCommand source and re-compiling the Microsoft.Practices.Composite.Presentation.dll.
1) Download the Prism 1.2 source code and open the CompositeApplicationLibrary_Desktop.sln. In here is a Composite.Presentation.Desktop project that contains the DelegateCommand source.
2) Under the public event EventHandler CanExecuteChanged, modify to read as follows:
public event EventHandler CanExecuteChanged
{
add
{
WeakEventHandlerManager.AddWeakReferenceHandler( ref _canExecuteChangedHandlers, value, 2 );
// add this line
CommandManager.RequerySuggested += value;
}
remove
{
WeakEventHandlerManager.RemoveWeakReferenceHandler( _canExecuteChangedHandlers, value );
// add this line
CommandManager.RequerySuggested -= value;
}
}
3) Under protected virtual void OnCanExecuteChanged(), modify it as follows:
protected virtual void OnCanExecuteChanged()
{
// add this line
CommandManager.InvalidateRequerySuggested();
WeakEventHandlerManager.CallWeakReferenceHandlers( this, _canExecuteChangedHandlers );
}
4) Recompile the solution, then navigate to either the Debug or Release folder where the compiled DLLs live. Copy the Microsoft.Practices.Composite.Presentation.dll and .pdb (if you wish) to where you references your external assemblies, and then recompile your application to pull the new versions.
After this, CanExecute should be fired every time the UI renders elements bound to the DelegateCommand in question.
Take care,
Joe
refereejoe at gmail
After reading some good answers to similar questions I changed in your example the DelegateCommand slightly to make it work. Instead of using:
public event EventHandler CanExecuteChanged;
I changed it to:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
I removed the following two methods because I was too lazy to fix them
public void RaiseCanExecuteChanged()
and
protected virtual void OnCanExecuteChanged()
And that's all... this seems to ensure that CanExecute will be called when the Binding changes and after the Execute method
It will not automatically trigger if the ViewModel is changed but as mentioned in this thread possible by calling the CommandManager.InvalidateRequerySuggested on the GUI thread
Application.Current?.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)CommandManager.InvalidateRequerySuggested);
In .NET 7.0 RC1 this has been fixed.
(At least in a sense...)
It should now automatically reevaluate CanExecute() whenever CommandParameter changes, including upon initialization.
Although that does not prevent the initial call to CanExecute() when CommandParameter is still null, many ICommand implementations should already be handling that and it does render obsolete the obscure and problematic XAML attribute ordering workaround / hack.
As intimated by #Daniel-Svensson in a GitHub comment:
the actual issue here is that ICommand.CanExecute is not reevaluated
when the value bound to CommandParameter changes. Doing so would
obviously be the correct behavior, since the command parameter is
passed to CanExecute, so everyone expects this behavior intuitively.
And that is what is being fixed.
According to #pchaurasia14, a Sr. Engineering Manager for WPF at Microsoft:
This has been fixed in RC1 release. You may try it out. ... I meant, .NET 7 RC1.
The GitHub tracking issue #316 in the dotnet/wpf project is listed as closed. The code change CommandParameter invalidates CanExecute #4217 has been included in .NET 7.0 RC1. It was merged on July 21, 2022 and is included in the list of commits (scroll way down) for the RC1 release.
I've logged this as a bug against WPF in .Net 4.0, as the problem still exists in Beta 2.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=504976
Some of these answers are about binding to the DataContext to get the Command itself, but the question was about the CommandParameter being null when it shouldn't be. We also experienced this. On on a hunch, we found a very simple way to get this to work in our ViewModel. This is specifically for the CommandParameter null problem reported by the customer, with one line of code. Note the Dispatcher.BeginInvoke().
public DelegateCommand<objectToBePassed> CommandShowReport
{
get
{
// create the command, or pass what is already created.
var command = _commandShowReport ?? (_commandShowReport = new DelegateCommand<object>(OnCommandShowReport, OnCanCommandShowReport));
// For the item template, the OnCanCommand will first pass in null. This will tell the command to re-pass the command param to validate if it can execute.
Dispatcher.BeginInvoke((Action) delegate { command.RaiseCanExecuteChanged(); }, DispatcherPriority.DataBind);
return command;
}
}
not sure if this will work in a data template, but here is the binding syntax I use in a ListView Context menu to grab the current item as a command parameter:
CommandParameter=
"{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},
Path=PlacementTarget.SelectedItem,
Mode=TwoWay}"
Its a long shot. to debug this you can try:
- checking the PreviewCanExecute event.
- use snoop/wpf mole to peek inside and see what the commandparameter is.
HTH,
The commandManager.InvalidateRequerySuggested works for me as well. I believe the following link talks about similar problem, and M$ dev confirmed the limitation in the current version, and the commandManager.InvalidateRequerySuggested is the workaround. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/
What important is the timing of invoking the commandManager.InvalidateRequerySuggested. This should be invoked after the relevant value change is notified.
Beside Ed Ball's suggestion on setting CommandParameter prior to Command, make sure your CanExecute method has a parameter of object type.
private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)
{
// Your goes heres
}
Hope it prevents someone spending the huge amount of time I did to figure out how to receive SelectedItems as CanExecute parameter

Resources