Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WinRT] Use a queue to prevent multiple MessageDialogs from causing a crash #347

Merged
merged 2 commits into from Dec 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,37 @@
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using System;
using System.Threading.Tasks;

#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
#endif

namespace Xamarin.Forms.Controls
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Bugzilla, 43469, "Calling DisplayAlert twice in WinRT causes a crash", PlatformAffected.WinRT)]
public class Bugzilla43469 : TestContentPage
{
protected override void Init()
{
var button = new Button { Text = "Click to call DisplayAlert twice" };

button.Clicked += (sender, args) =>
{
Device.BeginInvokeOnMainThread(new Action(async () =>
{
await DisplayAlert("First", "Text", "Cancel");
}));

Device.BeginInvokeOnMainThread(new Action(async () =>
{
await DisplayAlert("Second", "Text", "Cancel");
}));
};

Content = button;
}
}
}
Expand Up @@ -130,6 +130,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla42364.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla42519.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla43313.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla43469.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla43516.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla43663.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla44944.cs" />
Expand Down
37 changes: 35 additions & 2 deletions Xamarin.Forms.Platform.WinRT/Platform.cs
Expand Up @@ -761,8 +761,41 @@ async void OnPageAlert(Page sender, AlertArguments options)
dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

IUICommand command = await dialog.ShowAsync();
options.SetResult(command.Label == options.Accept);
if (Device.IsInvokeRequired)
{
Device.BeginInvokeOnMainThread(async () =>
{
IUICommand command = await dialog.ShowAsyncQueue();
options.SetResult(command.Label == options.Accept);
});
}
else
{
IUICommand command = await dialog.ShowAsyncQueue();
options.SetResult(command.Label == options.Accept);
}
}
}

// refer to http://stackoverflow.com/questions/29209954/multiple-messagedialog-app-crash for why this is used
// in order to allow for multiple MessageDialogs, or a crash occurs otherwise
public static class MessageDialogExtensions
{
static TaskCompletionSource<MessageDialog> _currentDialogShowRequest;

public static async Task<IUICommand> ShowAsyncQueue(this MessageDialog dialog)
{
while (_currentDialogShowRequest != null)
{
await _currentDialogShowRequest.Task;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks strange to me. As the signature is not async void the control won't return to the caller while this call is awaiting.

I'd rewrite this method to be public static Task<IUICommand> ShowAsyncQueue(this MessageDialog dialog) and fix this...

unless this is the desired behavior, obviously

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as the caller is async void, this is probably fine... for this time


var request = _currentDialogShowRequest = new TaskCompletionSource<MessageDialog>();
var result = await dialog.ShowAsync();
_currentDialogShowRequest = null;
request.SetResult(dialog);

return result;
}
}
}