简体   繁体   English

CanExecute 仅部分起作用

[英]CanExecute works only partial

I am working on a WPF .NET 5 application that needs to handle a longer task using a button command.我正在开发一个 WPF .NET 5 应用程序,它需要使用按钮命令处理更长的任务。 Until the task is done, the button should be disabled.在任务完成之前,该按钮应该被禁用。 I am using the RelayCommand from Microsoft.Toolkit.Mvvm:我正在使用 Microsoft.Toolkit.Mvvm 的 RelayCommand:
BtnCmd = new RelayCommand(DoSomething, CanDoSomething);
The first thing the DoSomething method does is make the return value of CanDoSomething false. DoSomething 方法所做的第一件事是使CanDoSomething的返回值为假。 This prevents DoSomething from being executed again, but it is not visually visible on the Button.这可以防止再次执行 DoSomething,但它在 Button 上不可见。
While researching, I came across that this is indeed the case on https://github.com/CommunityToolkit/MVVM-Samples/issues/41 :在研究时,我发现https://github.com/CommunityToolkit/MVVM-Samples/issues/41上确实是这种情况:

"Sergio0694 commented on Mar 28, 2021" : "That is correct, by default RelayCommand will not automatically update its visual state on WPF...". “Sergio0694 于 2021 年 3 月 28 日发表评论” :“没错,默认情况下,RelayCommand 不会在 WPF 上自动更新其视觉对象 state...”。

The solution he recommends is using: https://gist.github.com/Sergio0694/130bc344e552e501563546454bd4e62a and他推荐的解决方案是使用: https://gist.github.com/Sergio0694/130bc344e552e501563546454bd4e62a

<button xmlns:input="using:Microsoft.Toolkit.Mvvm.Wpf.Input"    
        Command="{Binding Command}"
        input:RelayCommandExtensions.IsCommandUpdateEnabled="True"/> 

My DoSomething Mehod looks like this:我的 DoSomething 方法如下所示:

private async void DoSomething()
{
    PropertyCheckedByCanDoSomething = false;
    await Task.Delay(1000);
    PropertyCheckedByCanDoSomething = true;
}

It will give the desired visual effect, but only on the line: PropertyCheckedByCanDoSomething = false;它将提供所需的视觉效果,但仅在线: PropertyCheckedByCanDoSomething = false; With PropertyCheckedByCanDoSomething = true;使用PropertyCheckedByCanDoSomething = true; the effect is only visible after clicking into the application or doing a window switch.该效果仅在单击应用程序或进行 window 切换后可见。
How can I fix this?我怎样才能解决这个问题?
Thanks a lot for any support.非常感谢您的支持。

Without knowing how all your bindings and such are going, with I might approach it by subclassing your relay command, something like在不知道所有绑定等情况的情况下,我可能会通过子类化您的中继命令来接近它,例如

using System;
using System.Windows.Input;
namespace MyTestApp
{
    public class MyRelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;

        public MyRelayCommand(Action<object> execute) : this(execute, CanAlwaysExecute)
        { }

        public MyRelayCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            // Lamda expression to execute each respectively
            _execute = execute;
            _canExecute = canExecute;
        }

        // The CanExecuteChanged event handler is required from the ICommand interface
        public event EventHandler CanExecuteChanged;
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, new EventArgs());
        }

        public bool CanExecute(object cmdParm)
        { return _isMyTaskRunning && _canExecute(cmdParm); }

        public static bool CanAlwaysExecute(object cmdParm)
        { return true; }

        public void Execute(object cmdParm)
        {
            // prevent double action if running vs not
            if (_isMyTaskRunning)
                return;

            // flag it to prevent double action
            _isMyTaskRunning = true;

            // trigger raising the CanExecute changed which will update the user interface
            RaiseCanExecuteChanged();
    
            // turn off when done, but if being done from a "task()" process, 
            // you probably want to have a return function be called when the 
            // TASK is finished to re-enable the button... maybe like
            System.Threading.Tasks.Task.Run(() => _execute)
                                    .ContinueWith(task => { ButtonTaskIsComplete(); } );
        }

        private bool _isMyTaskRunning = false;
        public void ButtonTaskIsComplete()
        {
            _isMyTaskRunning = false;
            RaiseCanExecuteChanged();
        }


    }
}

May not be a perfect fit, but might offer a possible wrapper solution for you.可能不是一个完美的选择,但可能会为您提供一个可能的包装解决方案。

I had a similar problem with that RelayCommandExtension you refered to.您提到的RelayCommandExtension也有类似的问题。

There exists a Github issue regarding this.存在与此相关的问题 Github The following solution was posted there:那里发布了以下解决方案

In RelayCommandExtensions.cs add the following line:RelayCommandExtensions.cs添加以下行:

notifiers.Add(obj, notifier);

to the OnIsCommandUpdateEnabledChanged(...) method so that it looks like that:OnIsCommandUpdateEnabledChanged(...)方法,使其看起来像这样:

// Ensure a notifier is enabled so that we can monitor for changes to the command
// property in the future. This is also needed in case this attached property is
// set before the binding engine has actually resolved the target command instance.
if (!notifiers.TryGetValue(obj, out _))
{
    PropertyChangeNotifier notifier = new(obj, nameof(ICommandSource.Command));

    notifier.PropertyChanged += (_, _) => ToggleIsCommandUpdateEnabled(obj);
    
    notifiers.Add(obj, notifier);
}

This is doing the trick for me.这对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM