2016-11-08 74 views
2

我有一個ViewModel,它使用Commanding。在此ViewModel我想訪問MediaPlugin 。有了這個插件,我不得不打電話給Initialize()。因爲它使用async調用,我有一些計時問題。從ViewModel調用異步方法

這是我的代碼:

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

這段代碼的應用程序崩潰與

調用任何屬性之前,必須調用初始化()。
在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 Mobile設備(Windows 10 Universal)上的Xamarin.Forms環境中測試此代碼。

+0

你能分享MyViewModel的用法嗎? –

+0

它用作我的XAML的['BindingContext'](https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_binding_basics/)。 – testing

+0

是的,你是否在Initialize之前調用了BindingContex?在xmal構造函數是類似的東西? public LoginPage() { vm = new LoginPageViewModel(); BindingContext = vm; InitializeComponent(); } –

回答

2

您還沒有提供Minimal, Complete, and Verifiable code example,特別是您沒有顯示您的Command類的實現。所以我在這裏假設它是你引用的Xamarin文章中描述的Command類。

您的視圖模型的異步初始化的基本策略是這樣的:

  1. 禁用依賴於初始化的結果的任何命令,清除任何屬性(或設置爲相應的默認值)依賴於初始化結果。
  2. 開始異步初始化。
  3. 使用一些延續機制(即不會阻塞線程)等待異步初始化完成。
  4. 初始化完成後,重新啓用先前禁用的命令並更新任何相關屬性。

根據您發佈的代碼,在我看來,缺少的主要是步驟#4。即你沒有做任何事情來重新啓用以前禁用的命令,即使你有一個方便的地方這樣做:

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

Command.ChangeCanExecute()方法引發CanExecuteChanged事件,使綁定到命令控制可在CanExecute()方法的結果將不同時通知。通過在完成初始化時調用此方法,即使初始化完成後,也應該解決按鈕保持禁用的問題。另外

一個注意:我不電話InitMedia()方法,或做任何初始化可言,從CanExecuteCameraCommand()方法。由於初始化是異步的,因此無論如何您都可以在從該方法返回之前成功進行初始化,並且您已經從構造函數調用了初始化。 ICommand.CanExecute()的實現應該非常簡單,並嚴格限於檢查事物的當前狀態並根據這些結果返回所需的值。

如果上述內容不能解決您的問題,請通過提供一個好的MCVE來改善問題,並更詳細地解釋發生了什麼問題,您嘗試解決問題的方法以及您遇到什麼問題搞清楚。

+0

'Command'實現['ICommand'接口](https://msdn.microsoft.com/en-us/library/system.windows.input.icommand(v = vs.110).aspx)。我得到了和你一樣的結論,我必須重新檢查這個狀態。再次調用視圖模型的'CanExecuteCameraCommand()'是沒有用的,因爲結果不會在任何地方使用。在'ICommand'接口中沒有定義'ChangeCanExecute()'方法。所以如果可能的話,我應該改變'Command'類嗎?或者我可以以某種方式觸發'CanExecuteChanged'事件? – testing

+0

我認爲我必須執行Xamarin文章中提到的'Command',而不是'ICommand' ... – testing

+1

_「命令實現了ICommand接口」_ - 是的,但它是_ Command_類,類(假設你使用[Xamarin一個](https://developer.xamarin.com/api/type/Xamarin.Forms.Command/))_does_有一個'ChangeCanExecute()'方法。你只需要確保你的'_cameraCommand'變量的類型是'Command'而不是'ICommand'。你不需要實現任何東西;你正在使用的'Command'類已經做到了這一點......只要確保你沒有通過在「ICommand」變量中存儲實例引用來限制自己。 –