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
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.
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 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:
Ignoring the
Tag
-based workaround for the special case of context menu, the key here is to bind theCommandParameter
regularly, but bind theCommand
with the additionalIsAsync=True
. This will delay the binding of the actual command (and therefore itsCanExecute
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.After reading some good answers to similar questions I changed in your example the DelegateCommand slightly to make it work. Instead of using:
I changed it to:
I removed the following two methods because I was too lazy to fix them
and
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
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:
New Code:
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).
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 theCommandParameter
cause theCommand
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 callingPropertyDescriptor.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 useRoutedCommand
s here (we use Prism'sDelegateCommand<T>
for pretty much everything) so please don't hold me responsible if my call toCommandManager.InvalidateRequerySuggested
sets off some sort of quantum wavefuntion collapse cascade that destroys the known universe or anything.