1

I have just updated a project from Catel 3.4 to Catel 4.0 and a custom apply button that had been working now never gets enabled.

AddCustomButton(new DataWindowButton("Apply", ExecuteApply, canExecuteApply));

In Catel 3.4 the canExecuteApply got called when the window got focus or any control was changed. In 4.0 it gets called twice when the window is created and never again.

I suspect this has something to do with the IViewPropertySelector part of the update guide, however registering the default implementation had no effect and I can't figure out what namespace the AutoDetectViewPropertiesToSubscribe extension method is in.

Edit: I have found I am getting the same behavior with some AsynchronousCommand instances elsewhere in the application. The CanExecute delegate fires when the control is created then never again.

Edit 2: These were the same issue with diffrent solutions. For an explanation of the issue see Geert van Horrik's answer.

If the command is registered in a view model you can use

ViewModelCommandManager.InvalidateCommands(true);

to get the can execute state to re-evaluate. For a DataWindowButton as described above I had to manually call RaiseCanExecuteChanged on the button's command since that command does not belong to a vie model as far as i can tell.

var catelCommand = (applyButton.Command as ICatelCommand);
if (catelCommand != null)
{
    catelCommand.RaiseCanExecuteChanged();
}

In either case, this is far from the approach with the best performance characteristics, but if the same behavior you had before the upgrade is desired, you can make these calls in the following event subscription:

System.Windows.Input.CommandManager.RequerySuggested += RequerySuggested;

Hope this helps anyone else facing this issue.

Dwayne
  • 73
  • 7

2 Answers2

2

The reason is that in the past (pre 4.0), Catel subscribed to the CommandManager of WPF and invalidated all the commands on all view models on nearly everything (mouse move, focus, etc). To improve performance (a lot), we decided to only invalidate commands automatically when a property changes on a specific view model.

For example, if you have a vm where you change a property, it will automatically re-evaluate the commands on that vm. You can still manually re-evaluate commands using this code (inside a vm):

ViewModelCommandManager.InvalidateCommands(true);
Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • For the explicit AsynchronousCommand instances in view models this worked. Did it in a subscription to System.Windows.Input.CommandManager.RequerySuggested to restore the old behavior for those VMs. However where do I do this for the DataWindowButton? – Dwayne Nov 19 '14 at 17:07
  • From the top of my head it does implement the same logic. Otherwise please check out the source, that's the power of open source ;-) – Geert van Horrik Nov 19 '14 at 20:58
  • So what would you suggest if you have a nested usercontrol and when the user control's properties changes, commands on the outher view must be invalidated? – pjdupreez Jan 07 '15 at 11:34
  • I played around a bit and found another way... see my answer and tell me what you think, please. – pjdupreez Jan 07 '15 at 12:25
  • I was loosing my mind for hours before I found this. – craftworkgames Nov 28 '15 at 11:47
1

How about this? I had a problem where my nested user controls must cause my outer user control's commands to update. It is not elegant but it gets the job done for me, until I can get a better way.

public partial class App : Application
{

    private static IViewModelManager _ViewModelManager;

    public App()
        : base()
    {
        var dependencyResolver = this.GetDependencyResolver();

        _ViewModelManager = dependencyResolver.Resolve<IViewModelManager>();

        System.Windows.Input.CommandManager.RequerySuggested += RequerySuggested;
    }

    private void RequerySuggested(object sender, EventArgs e)
    {
        foreach (IViewModel viewModel in _ViewModelManager.ActiveViewModels)
        {
            (viewModel as ViewModelBase).GetViewModelCommandManager().InvalidateCommands(true);
        }
    }
}
pjdupreez
  • 659
  • 1
  • 8
  • 19
  • This is a working solution as well. Just make sure that this doesn't impact performance too much. – Geert van Horrik Jan 07 '15 at 15:22
  • yeah, I did notice a slight performance issue, but not much. If I understand it right, only the active viewmodels should be affected, but would this differ from how it worked before v4.0? – pjdupreez Jan 08 '15 at 05:38
  • 1
    The thing is that RequestSuggested is fired a lot (and I mean a *lot*). There is a reason we changed this behavior in Catel 4.0 (it was using RequerySuggested before on commands). – Geert van Horrik Jan 08 '15 at 10:15