【问题标题】:How to call an asynchronous method when pressing a button using MVVM?使用MVVM按下按钮时如何调用异步方法?
【发布时间】:2020-05-16 04:48:18
【问题描述】:

我现在一直在寻找一种将命令绑定到按钮的方法,该按钮应该在我的 ViewModel 中提示一个异步函数,该函数应该启动调用并能够取消调用。我查看了 Stephen Cleary 的教程并尝试将它们转换为我的需要,尽管命令管理器在 AsyncCommandBase 的当前上下文中不存在,当您查看他的 git 项目代码时,它与他的教程中的完全不同...我不知道从哪里继续得到我的答案,所以我们开始吧。我有一个 ViewModel 应该运行一个异步的函数并且应该通过单击一个按钮来运行?有没有办法在不编写新库的情况下完成这项工作?我做了一个看起来像这样的界面......

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
using System.Threading.Tasks; 

    namespace Data
    {
        public interface IAsyncCommand : ICommand
        {
            Task ExcecuteAsync(Object parameter); 
        }
    }

和一个看起来像这样的基本命令类

using System;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Data
{
    /// <summary>
    /// An async command that implements <see cref="ICommand"/>, forwarding <see cref="ICommand.Execute(object)"/> to <see cref="IAsyncCommand.ExecuteAsync(object)"/>.
    /// </summary>
    public abstract class AsyncCommandBase : IAsyncCommand
    {
        /// <summary>
        /// The local implementation of <see cref="ICommand.CanExecuteChanged"/>.
        /// </summary>
        private readonly ICanExecuteChanged _canExecuteChanged;

        /// <summary>
        /// Creates an instance with its own implementation of <see cref="ICommand.CanExecuteChanged"/>.
        /// </summary>
        protected AsyncCommandBase(Func<object, ICanExecuteChanged> canExecuteChangedFactory)
        {
            _canExecuteChanged = canExecuteChangedFactory(this);
        }

        /// <summary>
        /// Executes the command asynchronously.
        /// </summary>
        /// <param name="parameter">The parameter for the command.</param>
        public abstract Task ExecuteAsync(object parameter);

        /// <summary>
        /// The implementation of <see cref="ICommand.CanExecute(object)"/>.
        /// </summary>
        /// <param name="parameter">The parameter for the command.</param>
        protected abstract bool CanExecute(object parameter);

        /// <summary>
        /// Raises <see cref="ICommand.CanExecuteChanged"/>.
        /// </summary>
        protected void OnCanExecuteChanged()
        {
            _canExecuteChanged.OnCanExecuteChanged();
        }

        event EventHandler ICommand.CanExecuteChanged
        {
            add { _canExecuteChanged.CanExecuteChanged += value; }
            remove { _canExecuteChanged.CanExecuteChanged -= value; }
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute(parameter);
        }

        async void ICommand.Execute(object parameter)
        {
            await ExecuteAsync(parameter);
        }
    }
}

链接指向他的教程,如果你需要它告诉我,我会尽快发送他的 git 代码! :D

https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/april/async-programming-patterns-for-asynchronous-mvvm-applications-commands

【问题讨论】:

    标签: c# mvvm


    【解决方案1】:

    我不明白您为什么需要创建 RelayCommand 的异步版本。您可以在执行侦听取消令牌的命令时简单地运行异步方法。比如:

    您的命令:public ICommand DoSomethingCommand { get; set; }

    在某处实例化您的命令:DoSomethingCommand = new RelayCommand(DoSomething);

    还有一个DoSomething方法的例子:

    private async void DoSomething()
    {
        using (var cancellationToken = new CancellationTokenSource())
        {
            await Task.Run(() => 
            {
                for(i = 0; i < 100; i++)
                {
                    if (cancellationToken.IsCancellationRequested) break;
                    //Here's where something is done.
                }
            });
        }
    }
    

    正常绑定:&lt;Button Content="Do Something" Command="{Binding DoSomethingCommand}"/&gt;

    以防万一,您可以使用简单的RelayCommand

    public class RelayCommand : ICommand
        {
            public RelayCommand(Action execute, Func<bool> canExecute)
            {
                CommandManager.RequerySuggested += (s, e) => CanExecuteChanged(s, e);
                CanExecuteDelegate = canExecute;
                ExecuteDelegate = execute;
            }
            public RelayCommand(Action execute) : this(execute, () => true) { }
            public event EventHandler CanExecuteChanged = delegate { };
            public Func<bool> CanExecuteDelegate { get; set; }
            public Action ExecuteDelegate { get; set; }
            public bool CanExecute(object parameter) => CanExecuteDelegate();
            public void Execute(object parameter) => ExecuteDelegate();
        }
    

    我确实建议使用适当的库,例如 MVVM Light。希望这会有所帮助。

    【讨论】:

    • 这会被视为遵循 mvvm 风格吗?
    • 还有什么方法可以使用异步函数的参数来从 ui 中获取信息?
    • 请放心,这确实遵循 MVVM 模式 :)。关于参数,我之前提到的RelayCommand 的实现不支持参数,但你可以冷酷地适应它。我认为,更简单的方法是将要作为参数传递的属性添加到视图模型并将它们绑定到 UI。当 UI 更新时,视图模型中的属性也会更新。您的视图模型中的方法可以读取这些属性,因为它们属于同一个类,因此您不必特别将它们作为参数传递给您的命令。
    • 啊谢谢帮助:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-11
    • 2013-04-01
    • 2012-12-26
    • 2023-03-31
    • 2011-06-14
    • 1970-01-01
    • 2019-01-10
    相关资源
    最近更新 更多