等待DisplayActionSheet(...)立即在Windows上返回null

时间:2016-06-09 10:02:12

标签: c# android xamarin xamarin.android xamarin.forms

我需要从ViewModel显示MessageDialog,我需要将Action与按下的按钮相关联。为此我写了以下内容:

//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就不会变得等待。

2 个答案:

答案 0 :(得分:1)

我的猜测是,在Android上,Action中的BeginInvokeOnMainThread会立即执行,而Result会分配一个值。在Windows上,Action方法可能在调用BeginInvokeOnMainThread方法后立即执行。

你真的不应该在另一个线程上做一个依赖于结果的动作,正如你在这里做的那样。相反,您应该等待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)); } 方法中,然后创建一个异步包装器:

{{1}}

答案 1 :(得分: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();
}