Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7838979
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 2, 20262026-06-02T15:16:41+00:00 2026-06-02T15:16:41+00:00

I am trying to understand why CanExecute is invoked on a command source that

  • 0

I am trying to understand why CanExecute is invoked on a command source that has been removed from the UI. Here is a simplified program to demonstrate:

<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-behind:

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 “Remove first item” button one time, I get this output:

Removed item marked 'Removed'
Item removed
Why is this happening?
Why is this happening?

“Why is this happening?” keeps being printed each time I click on some empty part of the window.

Why is this happening? And what can or should I do to prevent CanExecute from being invoked on removed command sources?

Note: RelayCommand can be found here.

Answers to Michael Edenfield questions:

Q1: Callstack of when CanExecute is invoked on removed button:

WpfApplication1.exe!WpfApplication1.MainWindow.Foo.get_Act.AnonymousMethod__1(object param) Line 30
WpfApplication1.exe!WpfApplication1.RelayCommand.CanExecute(object parameter) Line 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.RaiseRequerySuggested(object obj) + 0xf bytes

Q2: Also, does this keep happening if you remove all of the buttons from the list (not just the first?)

Yes.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-02T15:16:43+00:00Added an answer on June 2, 2026 at 3:16 pm

    The issue is that the command source (i.e. the button) does not unsubscribed from CanExecuteChanged of the command it is bound to, so that whenever CommandManager.RequerySuggested fires, CanExecute fires as well, long after the command source is gone.

    To solve this I implemented IDisposable on RelayCommand, and added the necessary code so that whenever a model object is removed, and so is removed from the UI, Dispose() is invoked
    on all its RelayCommand.

    This is the modified RelayCommand (the original is here):

    public class RelayCommand : ICommand, IDisposable
    {
        #region Fields
    
        List<EventHandler> _canExecuteSubscribers = new List<EventHandler>();
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;
    
        #endregion // Fields
    
        #region Constructors
    
        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }
    
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
    
            _execute = execute;
            _canExecute = canExecute;
        }
    
        #endregion // Constructors
    
        #region ICommand
    
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
    
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
                _canExecuteSubscribers.Add(value);
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
                _canExecuteSubscribers.Remove(value);
            }
        }
    
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    
        #endregion // ICommand
    
        #region IDisposable
    
        public void Dispose()
        {
            _canExecuteSubscribers.ForEach(h => CanExecuteChanged -= h);
            _canExecuteSubscribers.Clear();
        }
    
        #endregion // IDisposable
    }
    

    Wherever I use the above, I track all instantiated RelayCommands so I can invoke Dispose() when the time comes:

    Dictionary<string, RelayCommand> _relayCommands 
        = new Dictionary<string, RelayCommand>();
    
    public ICommand SomeCmd
    {
        get
        {
            RelayCommand command;
            string commandName = "SomeCmd";
            if (_relayCommands.TryGetValue(commandName, out command))
                return command;
            command = new RelayCommand(
                param => {},
                param => true);
            return _relayCommands[commandName] = command;
        }
    }
    
    void Dispose()
    {
        foreach (string commandName in _relayCommands.Keys)
            _relayCommands[commandName].Dispose();
        _relayCommands.Clear();
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Trying to understand this open source app on github, it has a gem file:
While trying to understand Rvalue references from here , I am unable to understand
Just trying to understand that - I have never used it before. How is
Been trying to understand shared pointer for a few days now and it feels
I'am trying to understand the example from program_options of the boost library ( http://www.boost.org/doc/libs/1_38_0/doc/html/program_options/tutorial.html#id3761458
I'm trying to understand if it's possible to create a set of variables that
Trying to understand something that I don't know how to describe because I don't
Trying to understand how to link a function that is defined in a struct,
I am trying understand how multi queries work in mysqli. But I confess that
Trying to understand why rownames = FALSE is not passed on from Test to

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.