【问题标题】:Calling async method from ViewModel从 ViewModel 调用异步方法
【发布时间】:2016-11-08 16:30:01
【问题描述】:

我有一个ViewModel,它使用Commanding。在这个ViewModel 中,我想访问MediaPlugin 。有了这个插件,我必须打电话给Initialize()。因为它使用async 调用,所以我有一些时间问题。

这是我的代码:

public ICommand CameraCommand
{
    get { return _cameraCommand ?? (_cameraCommand = new Command(async () => await ExecuteCameraCommand(), () => CanExecuteCameraCommand())); }
}

public bool CanExecuteCameraCommand()
{
    // Check if initialized before calling properties
    if (!this.initialized)
        InitMedia();

    if (!this.initialized || !CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
    {
        return false;
    }
    return true;
}

public async Task ExecuteCameraCommand()
{
    // Assure that it is initialized before calling method
    var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions {});
    // ...
}

private void InitMedia()
{
    CrossMedia.Current.Initialize();
    this.initialized = true;
}

使用此代码,应用程序崩溃

您必须在调用任何属性之前调用 Initialize()。
在 Plugin.Media.MediaImplementation.get_IsCameraAvailable()

当我像这段代码一样在构造函数中开始初始化时

public MyViewModel()
{
    InitData();
}

private async Task InitData()
{
    // ...
    await InitMedia();
}

private async Task InitMedia()
{
    await CrossMedia.Current.Initialize();
    this.initialized = true;
}

public bool CanExecuteCameraCommand()
{
    // Check if initialized before calling properties
    if (!this.initialized)
        return false;
    // ...
}

CanExecuteCameraCommand() 在初始化完成之前被调用。结果false 被返回并且UI 中的按钮被禁用。

我正在 Windows 10 移动设备(Windows 10 通用)上的 Xamarin.Forms 环境中测试此代码。

【问题讨论】:

  • 能分享一下MyViewModel的用法吗?
  • 它在我的 XAML 中用作BindingContext
  • 是的,你是在初始化之前调用 BindingContex 吗?在 xmal 构造函数中是这样的吗? public LoginPage() { vm = new LoginPageViewModel();绑定上下文 = vm;初始化组件(); }
  • 是的,我应该换那个吗?
  • 尝试类似的方法: public LoginPage() { vm = new LoginPageViewModel();绑定上下文 = vm;初始化组件(); vm.InitData();将其公开,并删除 InitData 表单构造函数 ogf 您的视图模型。

标签: c# xamarin async-await xamarin.forms uwp


【解决方案1】:

您没有提供Minimal, Complete, and Verifiable code example,特别是您没有展示您对Command 类的实现。所以我在这里假设它是您引用的 Xamarin 文章中描述的 Command 类。

视图模型异步初始化的基本策略是这样的:

  1. 禁用任何依赖于初始化结果的命令,清除任何依赖于初始化结果的属性(或设置为适当的默认值)。
  2. 开始异步初始化。
  3. 使用某种延续机制(即不要阻塞线程)等待异步初始化完成。
  4. 初始化完成后,重新启用之前禁用的命令并更新所有相关属性。

根据您发布的代码,在我看来,缺少的主要内容是第 4 步。 IE。你没有做任何事情来重新启用以前禁用的命令,即使你有一个方便的地方这样做:

private async Task InitMedia()
{
    await CrossMedia.Current.Initialize();
    this.initialized = true;
    _cameraCommand.ChangeCanExecute();
}

Command.ChangeCanExecute() 方法引发CanExecuteChanged 事件,以便当CanExecute() 方法的结果不同时,可以通知绑定到命令的控件。通过在初始化完成时调用此方法,这应该可以解决即使在初始化完成后按钮仍然处于禁用状态的问题。

还有一个注意事项:我不会CanExecuteCameraCommand() 方法调用InitMedia() 方法,或者根本不进行任何初始化。因为初始化是异步的,所以无论如何您都不能在从该方法返回之前成功初始化,并且您已经从构造函数中调用了初始化。 ICommand.CanExecute() 的实现应该非常简单,并且严格限于检查事物的当前状态并根据这些结果返回所需的 bool 值。

如果以上内容不能解决您的问题,请通过提供良好的 MCVE 来改进问题,并更准确地解释问题所在、您尝试解决的问题以及您具体遇到的问题出去。

【讨论】:

  • Command 实现了ICommand interface。我得出了和你一样的结论,我必须重新检查状态。再次调用视图模型的CanExecuteCameraCommand() 无济于事,因为结果不会在任何地方使用。在ICommand 接口中没有定义ChangeCanExecute() 方法。那么如果可能的话,我应该更改Command 类吗?或者我可以以某种方式触发CanExecuteChanged 事件吗?
  • 我想我必须实现 Xamarin 文章中提到的 Command 而不是 ICommand ...
  • "Command 实现了 ICommand 接口" -- 是的,但它 Command 类和那个类(假设你'重新使用the Xamarin one) 确实有一个ChangeCanExecute() 方法。您只需要确保您的_cameraCommand 变量的类型为Command 而不是ICommand。你不必实现任何东西;您正在使用的 Command 类已经这样做了...只要确保您不会通过将实例引用存储在 ICommand 变量中来限制自己。
  • 是的,我认为这将是解决问题的方法。完成后我会试一试并给你打勾。
猜你喜欢
  • 2017-10-18
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
  • 2017-09-23
  • 2017-03-06
  • 2017-11-11
  • 1970-01-01
相关资源
最近更新 更多