DataWindowButton CanExecute not firing, Catel 4.0

2019-08-02 21:30发布

问题:

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.

回答1:

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);


回答2:

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);
        }
    }
}


标签: c# wpf catel