2016-06-09 31 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不会等待。所以我的行动没有执行。 –