c# - 从 ViewModel 调用异步方法

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

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

这是我的代码:

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;
}

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

You must call Initialize() before calling any properties.
at 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 环境中测试此代码。

最佳答案

您尚未提供Minimal, Complete, and Verifiable code example ,特别是您还没有显示 Command 类的实现。因此,我在这里假设它是您引用的 Xamarin 文章中描述的 Command 类。

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

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

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

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

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

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

如果上述内容没有解决您的问题,请通过提供良好的 MCVE 来改进问题,并更准确地详细解释出现的问题、您尝试解决的问题以及您具体遇到的问题出来。

关于c# - 从 ViewModel 调用异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40491974/

相关文章:

python - 为什么使用 Python 异步读取和调用文件中的 API 比同步慢?

c# - 在现有模型对象之上实例化 ViewModels 和 Views

c# - 正则表达式嵌套括号

asp.net-web-api - Xamarin.iOS - WebApi 连接被拒绝

ios - 动画在 ViewWillAppear 中不起作用

ios - 解决 "' NSUnknownKeyException',原因:This class is not key value coding-compliant for the key X” exception

javascript - 在 JavaScript 间隔内等待

c# - 使用 TypedFactoryFacility 错误吗?

c# - 对于 vSphere 和 vCenter 自动化,PowerCLI cmdlet 还是 VIX API 是更好的方法?

javascript - 在 async/await 上返回多个变量