wpf: CommandParameter is null when call CanExecute method at first time
- .NET Core Version: (3.0.0-preview-27122-01)
- Windows version: (
windows 10
) - Does the bug reproduce also in WPF for .NET Framework 4.6.1?: Yes
Problem description:
I have a TestCommand as below:
public class TestCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
}
}
and then I use it in a xaml file:
<Button Content="Test" Command="{StaticResource testCommand}" CommandParameter="abcdef"/>
Actual behavior: At first time the CanExecute method is called, the parameter’s value is null。 If I change the XAML property’s order as below, the parameter’s value is abcdef (that’s ok):
<Button Content="Test" CommandParameter="abcdef" Command="{StaticResource testCommand}"/>
Expected behavior: The parameter’s value is not null at each time, regardless of the XAML order
Minimal repro:
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 16
- Comments: 29 (8 by maintainers)
Links to this issue
Commits related to this issue
- Delay first execution of ICommand.CanExecute(object parameter) until after initialization. Issue #316 Revert changes to Hyperlink.cs — committed to MichaelVach/wpf by MichaelVach 5 years ago
I think a better approach would be to first fix the behaviour of CommandParameter so that CanExecute is called whenever the parameter is changed. That should also solve the problems described here. Even if CanExecute would be called twice if parameters are in “wrong” order the end result would be the expected behaviour.
The behavior was fixed in silverlight but did never make it back to wpf (for backwards compatibility reasons i guess) so would really like to see it fixed for 3.1 so that CommanManager needs not be used for simple commands
Microsoft has shifted its focus to WinUI 3, give it up, rubbish WPF.
@DaveInCaz - I meant, .NET 7 RC1.
Can we have a fix for this broken behavior?
I remember running into this bug for over 8 years now and having to use a workaround. Putting
Command
beforeCommandParameter
(due to alphabetic ordering) gives me headaches every time.There should be a switch or something to apply this bug fix. Surely there will be workarounds from the last 14 years that will break.
This is truely ridiculous.
As @Daniel-Svensson said, the actual issue here is that
ICommand.CanExecute
is not reevaluated when the value bound toCommandParameter
changes. Doing so would obviously be the correct behavior, since the command parameter is passed toCanExecute
, so everyone expects this behavior intuitively.This causes issues beyond this one, for example when using
Button
inItemsControl
-like containers that use virtualization internally (which is currently impossible, since the enabled/disabled state of the button may refer to an entirely different item than the one the user sees).This affects WPF for .NET Framework as well, not only the new .NET Core variant.
@heku - This has been fixed in RC1 release. You may try it out.
If WinUI 3 focus on Win32 App, that will be difference.
I would like to propose a solution for the Button and MenuItem cases mentioned in the linked Stack Overflow question above.
Looking at stack trace that leads to the bug for the Button case
StackTrace leading to ICommand.CanExecute Bug.txt
we see that the error occurs in the PresentationFramework.dll!MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(System.Windows.Input.ICommandSource commandSource) method
The idea is to delay the execution of this method until the WPF elements are initialized. See the above linked commit 0c6b967 for the details.
@DaveInCaz - Yup, that’s the one I think it is.
@weltkante XAML loader is not the only way how an element can be constructed. The problem with moving to
ISupportInitialize
is that then you can file similar bug for everything that does not use that interface, rather than fixing it once at the receiving side.Either way, I think the compatibility favours Daniel’s solution (I actually consider it a bug that changing command parameter does not update the CanExecute state). You will still get the first call with null parameter, and the handler is expected to be called multiple times (not deterministically), so adding more calls shouldn’t be breaking.
@miloush this issue is about XAML attribute order and the WPF implementation of the XAML loader does use ISupportInitialize (the interface is implemented on FrameworkElement base class and will always be used when loading from XAML). So I don’t think your argument against using ISupportInitialize is valid.
That said I don’t mind the alternative solution suggested by @Daniel-Svensson
The .NET framework version of the xaml parser honours the ISupportInitialize interface (which is implemented by FrameworkElement), so its actually more like this: