如何使用视图模型调用异步命令?

时间:2017-09-08 11:15:17

标签: xamarin xamarin.forms

我有这个代码,我想把它移到一个视图模型中:

resetButton.Clicked += async (sender, e) =>
{
   if (App.totalPhrasePoints < 100 || await App.phrasesPage.DisplayAlert(
                "Reset score",
                "You have " + App.totalPhrasePoints.ToString() + " points. Reset to 0 ? ", "Yes", "No"))
      App.DB.ResetPointsForSelectedPhrase(App.cfs);
};

我意识到我需要设置这样的东西:

在我的XAML代码中;

<Button x:Name="resetButton" Text="Reset All Points to Zero" Command="{Binding ResetButtonClickedCommand}"/>

在我的C#代码中:

private ICommand resetButtonClickedCommand;

public ICommand ResetButtonClickedCommand
{
   get
   {
      return resetButtonClickedCommand ??
      (resetButtonClickedCommand = new Command(() =>
      {

      }));

    }

但是如何将异步动作放入命令中呢?

5 个答案:

答案 0 :(得分:5)

您可以尝试这样的事情:

RewriteEngine On
Options -Multiviews

答案 1 :(得分:0)

要扩展已经提供的答案,如果需要将参数传递给命令,则可以使用类似的

(resetButtonClickedCommand = new Command<object>(async (o) => await SomeMethod(o)));

async Task SomeMethod(object o)
{
    // do stuff with received object
}

您也可以将object替换为您想要的任何内容。

答案 2 :(得分:0)

下面是一个便利班。使用它可以简化与async的组合命令。

如果您有这样的async方法(从接受的答案中复制):

async Task SomeMethod()
{
    // do stuff
}

如果没有此类,则在async中使用该Command方法看起来像这样(从接受的答案中复制):

resetButtonClickedCommand = new Command(async () => await SomeMethod());

使用该类,可以简化用法:

resetButtonClickedCommand = new AsyncCommand(SomeMethod);

结果等效于不使用此类而显示的稍长的代码行。并不是什么大的好处,但是拥有隐藏混乱的代码,并为经常使用的概念起个名字是很好的。


使用带有参数的方法,其好处将变得更加明显:

async Task SomeMethod(object param)
{
    // do stuff
}

不上课:

yourCommand = new Command(async (param) => await SomeMethod(param));

具有类(与无参数情况相同;编译器将调用适当的构造函数):

yourCommand = new AsyncCommand(SomeMethod);

class AsyncCommand的定义:

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

namespace MyUtilities
{
    /// <summary>
    /// Simplifies using an "async" method as the implementor of a Command.
    /// Given "async Task SomeMethod() { ... }", replaces "yourCommand = new Command(async () => await SomeMethod());"
    /// with "yourCommand = new AsyncCommand(SomeMethod);".
    /// Also works for methods that take a parameter: Given "async Task SomeMethod(object param) { ... }",
    /// Usage: "yourCommand = new Command(async (param) => await SomeMethod(param));" again becomes "yourCommand = new AsyncCommand(SomeMethod);".
    /// </summary>
    public class AsyncCommand : ICommand
    {
        Func<object, Task> _execute;
        Func<object, bool> _canExecute;

        /// <summary>
        /// Use this constructor for commands that have a command parameter.
        /// </summary>
        /// <param name="execute"></param>
        /// <param name="canExecute"></param>
        /// <param name="notificationSource"></param>
        public AsyncCommand(Func<object,Task> execute, Func<object, bool> canExecute = null, INotifyPropertyChanged notificationSource = null)
        {
            _execute = execute;
            _canExecute = canExecute ?? (_ => true);
            if (notificationSource != null) 
            {
                notificationSource.PropertyChanged += (s, e) => RaiseCanExecuteChanged();   
            }
        }

        /// <summary>
        /// Use this constructor for commands that don't have a command parameter.
        /// </summary>
        public AsyncCommand(Func<Task> execute, Func<bool> canExecute = null, INotifyPropertyChanged notificationSource = null)
            :this(_ => execute.Invoke(), _ => (canExecute ?? (() => true)).Invoke(), notificationSource)
        {
        }

        public bool CanExecute(object param = null) => _canExecute.Invoke(param);

        public Task ExecuteAsync(object param = null) => _execute.Invoke(param);

        public async void Execute(object param = null)
        {
            // TBD: Consider adding exception-handling logic here.
            // Without such logic, quoting https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
            // "With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started."
            await ExecuteAsync(param);
        }

        public event EventHandler CanExecuteChanged;

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

}

答案 3 :(得分:0)

要使用参数实例化AsyncCommand,此方法是正确的:

this.SaveCommand = new AsyncCommand((o) => SaveCommandHandlerAsync (o));

还是需要

答案 4 :(得分:-1)

你也可以这样写: -

(resetButtonClickedCommand = new Command(DoSomething));

async void DoSomething()
{
    // do something
}

注意: - 它在SomeMethod上显示警告。