2

I am using the standard Josh Smith implementation of RelayCommand to implement ICommands in my WPF/MVVM application. See: WPF Apps with the MVVM Design Pattern

A few of my CanExecute methods take a long time to run. Individually, they aren't bad (a fraction of a second each). But I have noticed that the more commands that I bind to my UI, the slower the application responds (delays exceeding 10 seconds - yuck).

I believe it is because the RelayCommand CanExecuteChanged is hooking onto the CommandManager.RequerySuggested event and my ViewModel template calls CommandManager.InvalidateRequerySuggested() as part of the IDataErrorInfo validation.

My ViewModels implement IDisposable, so I have tried to take advantage of the OnDispose method to clean up my RelayCommands, but the ICommand.CanExecute methods continue to be called even after my VM is disposed of.

That leads me to conclude that something (the CommandManager?, the View?) is holding a reference to the RelayCommand.

Does anyone have a good pattern for releasing and disposing of RelayCommands to prevent them from being requeried after their desired lifetime has expired?

Is there a better way to manage when CanExecute should be called?

Paul Chavez
  • 895
  • 2
  • 13
  • 22
  • 1
    CanExecute is called quite frequently, so it's highly recommended that this be a fast calculation. If you have more complicated criteria, I recommend caching the result in a field or property based on a calculation at the time that the state changes, rather than at the time the state is requested. – Dan Bryant Aug 24 '11 at 17:26
  • So what you're saying is, "if it hurts to do that, don't do that". I think that is only half the answer. I agree that CanExecute gets called frequently and should respond quickly. Unfortunately, when dealing with concurrency, I still need to query the database to evaluate biz rules. Yes, this creates a race condition, but I'd rather not open the window wider by caching a validation result at the client. And that still doesn't answer the question of how to stop unneeded calls to CanExecute after the command has expired. – Paul Chavez Aug 24 '11 at 18:49
  • One suggestion for your current question is to modify RelayCommand to implement IDisposable so that you explicitly Dispose it and then ignore subsequent CanExecute requests once it has been disposed. It is a bit curious that it's still being called, though; is your View still bound to your (disposed) ViewModel? – Dan Bryant Aug 24 '11 at 19:43
  • I thought about implementing IDisposable on RelayCommand, but opted to extend the already implemented Dispose method of my ViewModel's base class to flag when dispose has been requested on the VM and shortcircuit the CanExecute call back methods. – Paul Chavez Aug 24 '11 at 20:58
  • As to the View binding to the ViewModel, I'm not sure when the View is disposed. All of my Views are implemented as user controls and hosted in a tab control. When the user requests a tab to close, I remove my TabViewModel from the ObservableCollection of databound tabitems. I don't explicitly dissociate the data binding and UserControl doesn't implement IDisposable, so I'm guessing they linger on the heap waiting for GC to kick in. Is that a reasonable assumption? – Paul Chavez Aug 24 '11 at 21:06
  • As a test, I tried setting .DataContext = null to one of my views to see if that would dissociate the binding to the VM and terminate the calls to the dependent Command's CanExecute methods. Nope. They still get called. – Paul Chavez Aug 24 '11 at 22:19

1 Answers1

0

You can take out the CommandManager.RequerySuggested and use MVVM Light's RelayCommand implementation. Note: this requires you to explicitly call RaiseCanExecuteChanged() anytime you want CanExecute to be updated.

Jake Berger
  • 5,237
  • 1
  • 28
  • 22