I am trying to understand why CanExecute is called in a command source that has been removed from the user interface. Here is a simplified demo program:
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="350" Width="525"> <StackPanel> <ListBox ItemsSource="{Binding Items}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <Button Content="{Binding Txt}" Command="{Binding Act}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Button Content="Remove first item" Click="Button_Click" /> </StackPanel> </Window>
Code for:
public partial class MainWindow : Window { public class Foo { static int _seq = 0; int _txt = _seq++; RelayCommand _act; public bool Removed = false; public string Txt { get { return _txt.ToString(); } } public ICommand Act { get { if (_act == null) { _act = new RelayCommand( param => { }, param => { if (Removed) Console.WriteLine("Why is this happening?"); return true; }); } return _act; } } } public ObservableCollection<Foo> Items { get; set; } public MainWindow() { Items = new ObservableCollection<Foo>(); Items.Add(new Foo()); Items.Add(new Foo()); Items.CollectionChanged += new NotifyCollectionChangedEventHandler(Items_CollectionChanged); DataContext = this; InitializeComponent(); } void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Remove) foreach (Foo foo in e.OldItems) { foo.Removed = true; Console.WriteLine("Removed item marked 'Removed'"); } } void Button_Click(object sender, RoutedEventArgs e) { Items.RemoveAt(0); Console.WriteLine("Item removed"); } }
When I click the "Delete first item" button once, I get this output:
Removed item marked 'Removed' Item removed Why is this happening? Why is this happening?
"Why is this happening?" It is constantly printed every time I click on some empty part of the window.
Why is this happening? And what can or should be done to prevent the use of CanExecute in remote command sources?
Note: RelayCommand can be found here .
Answers to Michael Edenfield's questions:
Q1: Callstack when CanExecute is called on a remote button:
WpfApplication1.exe! WpfApplication1.MainWindow.Foo.get_Act.AnonymousMethod__1 (object parameter) Line 30 WpfApplication1.exe! WpfApplication1.RelayCommand.CanExecute (object parameter) String 41 + 0x1a bytes PresentationFramework.dll! MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource (System.Windows.Input.ICommandSource commandSource) + 0x8a bytes PresentationFramework.dll! System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute () + 0x18 bytes PresentationFramework.dll! System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged (object sender, System.EventArgs e) + 0x5 bytes PresentationCore.dll! System.Windows.Input.CommandManager.CallWeakReferenceHandlers (System.Collections.Generic.List handlers) + 0xac bytes PresentationCore.dll! System.Windows.Input.CommandManager.RaiseRequerySposed (obj object) + 0xf bytes
Q2: In addition, this happens if you remove all the buttons from the list (and not just the first?)
Yes.
user610650
source share