2016-06-09 35 views
0

我需要顯示一個ViewModel中的MessageDialog,並且我需要將一個Action關聯到按下的按鈕上。爲了這個目的,我寫了下面的:等待DisplayActionSheet(...)立即在Windows上返回null

//Dummy implementation 
private string Translate(string element) => element; 

public async Task ShowAsync(string message, Dictionary<string, Action> commands) 
{ 
    var translatedCommands = new Dictionary<string, Action>(); 
    foreach (var element in commands) 
     translatedCommands.Add(Translate(element.Key), element.Value); 

    string selectedElement = null; 

    var buttons = new string[translatedCommands.Keys.Count]; 
    translatedCommands.Keys.CopyTo(buttons, 0); 

    Task<string> Result = null; 
    Device.BeginInvokeOnMainThread(() => 
    { 
     Result = App.Current.MainPage.DisplayActionSheet(message, null, null, buttons); 
    }); 

    selectedElement = await Result; 
    if (selectedElement == null) 
     return; 

    translatedCommands[selectedElement]?.Invoke(); 
} 

然後我把它從RelayCommand

private async void ExecuteButtonCommand(object p) 
     { 
      int Selectedindex = 0; 

      var messageCommands = new Dictionary<string, Action>() 
      { 
       { "Before 2003",() => 
        { 
         Selectedindex = 1; 
        } 
       }, 
       { "After 2003",() => 
        { 
         Selectedindex = 2; 
        } 
       }, 
      }; 

      await ShowAsync("Select period", messageCommands); 
      var dummy = Selectedindex; 
     } 

工作得非常好於Android,而是立即在Windows DisplayActionSheet返回null。我見過this so question,但它不適用於我,因爲如果我在Device.BeginInvokeOnMainThread之內等待ShowAsync不會變得等待。

回答

0

如果找到一個解決方案。

在Windows(電話)DisplayActionSheet不應該在MainThread中執行,但只是等待。

public async Task ShowAsync(string message, Dictionary<string, Action> commands) 
{ 
    var translatedCommands = new Dictionary<string, Action>(); 
    foreach (var element in commands) 
     translatedCommands.Add(Translate(element.Key), element.Value); 

    var buttons = new string[translatedCommands.Keys.Count]; 
    translatedCommands.Keys.CopyTo(buttons, 0); 


    string selectedElement = null; 

    if (Device.OS == TargetPlatform.Android) 
    { 
     Task<string> Result = null; 
     Device.BeginInvokeOnMainThread(() => 
     { 
      Result = App.Current.MainPage.DisplayActionSheet(message, null, null, buttons); 
     }); 

     selectedElement = await Result; 
    } 
    else if (Device.OS == TargetPlatform.Windows || Device.OS == TargetPlatform.WinPhone) 
    { 
     selectedElement = await App.Current.MainPage.DisplayActionSheet(message, null, null, buttons); 
    } 
    else 
     throw new NotImplementedException("Only implemented for Android and Windows (Phone)"); 

    if (selectedElement == null) 
     return; 

    translatedCommands[selectedElement]?.Invoke(); 
} 
1

我的猜測是,在Android上,ActionBeginInvokeOnMainThread立即執行,Result被分配一個值。在Windows上,只要BeginInvokeOnMainThread方法被調用,Action就可能不會執行。

你真的不應該在另一個線程上執行一個依賴於結果的動作,就像你在這裏做的那樣。相反,你應該要麼等待Result分配在主線程上執行ShowAsync的後半部分。

我推薦第二個選項是你沒有真正做二話不說從消費Result它被分配在主線程之後:

Device.BeginInvokeOnMainThread(() => 
{ 
    string selectedElement = await App.Current.MainPage.DisplayActionSheet(message, null, null, buttons); 
    if (selectedElement == null) 
     return; 

    translatedCommands[selectedElement]?.Invoke(); 
}); 

然而,這並不能使ShowAsync異步了。爲了使這一方法的異步版本,簡單地提取ShowAsync的編輯後的內容到同步Show方法,並創建一個異步包裝:

private void Show(string message, Dictionary<string, Action> commands) 
{ 
    // Code in ShowAsync is moved here 
} 

public Task ShowAsync(string message, Dictionary<string, Action> commands) 
{ 
    return Task.Run(() => Show(message, commands)); 
} 
+0

不是!它沒有工作。 Show不會等待消息對話框關閉,並且DisplayActionSheet不會等待。所以我的行動沒有執行。 –