C# 从ViewModel调用异步方法

C# 从ViewModel调用异步方法,c#,xamarin,async-await,xamarin.forms,uwp,C#,Xamarin,Async Await,Xamarin.forms,Uwp,我有一个视图模型,它利用了。在此ViewModel中,我想访问。有了这个插件,我必须打电话。因为它使用async调用,所以我有一些计时问题 这是我的代码: public ICommand CameraCommand { get { return _cameraCommand ?? (_cameraCommand = new Command(async () => await ExecuteCameraCommand(), () => CanExecuteCameraComma

我有一个
视图模型
,它利用了。在此
ViewModel
中,我想访问。有了这个插件,我必须打电话。因为它使用
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 Universal)上的Xamarin.Forms环境中测试此代码。

您没有提供,尤其是没有显示
命令
类的实现。所以我在这里假设它是您引用的Xamarin文章中描述的
命令

视图模型异步初始化的基本策略如下:

  • 禁用依赖于初始化结果的任何命令,清除依赖于初始化结果的任何属性(或设置为适当的默认值)
  • 开始异步初始化
  • 使用某种延续机制(即不要阻塞线程)等待异步初始化完成
  • 初始化完成后,重新启用以前禁用的命令并更新任何相关属性
  • 根据你发布的代码,我觉得缺少的主要是第4步。也就是说,即使您有一个方便的地方可以重新启用以前禁用的命令,您也没有执行任何操作:

    private async Task InitMedia()
    {
        await CrossMedia.Current.Initialize();
        this.initialized = true;
        _cameraCommand.ChangeCanExecute();
    }
    
    Command.ChangeCanExecute()
    方法引发
    CanExecuteChanged
    事件,以便当
    CanExecute()
    方法的结果不同时,可以通知绑定到该命令的控件。通过在初始化完成时调用此方法,可以解决即使在初始化完成后按钮仍处于禁用状态的问题

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


    如果上述内容不能解决您的问题,请通过提供一个好的MCVE来改进问题,并更详细地解释出现了什么问题,您试图解决什么问题,以及您在解决问题时遇到的具体问题。

    您能分享MyViewModel的用法吗?它与我的XAML一样使用。是的,在初始化之前是否调用BindingContex?在xmal构造函数中是这样的吗?public LoginPage(){vm=new LoginPageViewModel();BindingContext=vm;InitializeComponent();}是的,我应该切换吗?尝试这样的操作:public LoginPage(){vm=new LoginPageViewModel();BindingContext=vm;InitializeComponent();vm.InitData();}将其公开,并从viewmodel的构造函数ogf中删除InitData。命令
    实现了。我和你得出了同样的结论,我必须重新检查状态。再次调用视图模型的
    CanExecuteCameraCommand()
    没有帮助,因为结果没有在任何地方使用。在
    ICommand
    界面中,没有定义
    ChangeCanExecute()
    方法。如果可能的话,我是否应该更改
    命令
    类?或者我可以以某种方式触发
    CanExecuteChanged
    事件吗?我认为我必须实现Xamarin文章中提到的
    命令
    ,而不是
    ICommand
    。“该命令实现ICommand接口”——是的,但它是
    命令
    类,以及该类(假设您正在使用)没有
    ChangeCanExecute()
    方法。您只需确保
    \u cameraCommand
    变量的类型为
    命令
    ,而不是
    ICommand
    。你不需要实现任何东西;您正在使用的
    命令
    类已经做到了这一点……只需确保不要通过将实例引用存储在
    ICommand
    变量中来限制自己。是的,我认为这将是问题的解决方案。我会试试看,等我做完后给你打勾。