我是否可以扩展命令类,使与之绑定的按钮自动失效?

问题描述 投票:0回答:2

在我的应用程序中,我将我的按钮绑定到带有 "MVVM "的命令上,我的命令实现如下。

        public Command CommandLoadStuff
        {
            get
            {
                return new Command(async () =>
                {
                    await DoLongStuff();
                });
            }
        }

问题是这些命令是异步的,用户可以多次点击它们,导致代码也要执行多次。

作为第一个方法,我使用了CanExecute。

        public Command CommandLoadStuff
        {
            get
            {
                return new Command(async () =>
                {
                    AppIsBusy = true;
                    await DoLongStuff();
                    AppIsBusy = false;
                },() => !AppIsBusy);
            }
        }

现在我想,除了单独处理每个命令的CanExecute之外,是否还有更好的方法。

由于我每次都用 "new "来初始化命令,我想知道 "Command "类是否不能相应地扩展。它应该在CanExecute的生命周期内阻止按钮的第二次点击(可能是在构造函数中?(可能在Dispose函数中?)

有什么方法可以实现这一点吗?

c# xaml mvvm xamarin.forms
2个回答
1
投票

据我所知,这样扩展类命令是不可能的,因为 Execute 是非虚拟的,你必须通过 execute 动作到构造函数中。总之,还是有办法的。Command 派生自 ICommand 它的接口如下

public interface ICommand
{
    event EventHandler CanExecuteChanged;

    void Execute(object data);

    bool CanExecute(object data);
}

你可以创建一个类 AsyncBlockingCommand (或其他什么),将返回各自的值从 CanExecute 取决于异步方法是否仍在运行(我知道在 async void 方法,所以要小心处理)

public class AsyncBlockingCommand : ICommand
{
    bool _canExecute = true;
    Func<Task> _toExecute;

    public AsyncBlockingCommand(Func<Task> toExecute)
    {
        _toExecute = toExecute;
    }

    public event EventHandler CanExecuteChanged;

    public async void Execute(object data)
    {
        _canExecute = false;
        RaiseCanExecuteChanged();
        await _toExecute();
        _canExecute = true;
        RaiseCanExecuteChanged();
    }

    public bool CanExecute(object data) => _canExecute; 

    private void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

在你的异步方法执行之前。_canExecute 设置为 falseCanExecuteChanged 是提高。这样一来,你的 Button 会得到通知 CanExecute 的变化,并禁用自己。反之亦然。(可能是 RaiseCanExecuteChanged 必须在主线程上被调用)。)


0
投票

你可以使用 IsEnabled 属性,使 Button 不能点击,如以下代码。

<Button 
    Text="click"
    Command={Binding Button1Command}
    IsEnabled={Binding AreButtonsEnabled} />

如果 IsEnabled 是假的,你可以看到这个按钮,它是灰色的,如果你点击它,它将不会执行任何命令。

enter image description here

下面是MyViewModel的代码。

private bool _areButtonsEnabled = true;

public bool AreButtonsEnabled
{
    get => _areButtonsEnabled;
    set
    {
        if (_areButtonsEnabled != value)
        {
            _areButtonsEnabled = value;
            OnPropertyChanged(nameof(AreButtonsEnabled)); // assuming your view model implements INotifyPropertyChanged
        }
    }
}

public ICommand Button1Command { get; protected set; }

public MyViewModel()
{
    Button1Command = new Command(HandleButton1Tapped);
}

private void HandleButton1Tapped()
{
    // Run on the main thread, to make sure that it is getting/setting the proper value for AreButtonsEnabled
    // And note that calls to Device.BeginInvokeOnMainThread are queued, therefore
    // you can be assured that AreButtonsEnabled will be set to false by one button's command
    // before the value of AreButtonsEnabled is checked by another button's command.
    // (Assuming you don't change the value of AreButtonsEnabled on another thread)
    Device.BeginInvokeOnMainThread(async() => 
    {
        if (AreButtonsEnabled)
        {
            AreButtonsEnabled = false;
            // DoLongStuff code
            await  Task.Delay(2000);
            AreButtonsEnabled = true;
        }
    });
}
© www.soinside.com 2019 - 2024. All rights reserved.