Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple MessageDialog app crash

I use MessageDialogues at several places over my app. Problem is, whenever is any MessageDialog (or system alert, such as capability warning) active and another my MessageDialog is called, application crashes without exception or with UnathorizedAccessException.

This is, how I call MessageDialog:

CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    MessageDialog msg2 = new MessageDialog(_resourceLoader.GetString("MsgGPSUnavailable"));
    msg2.ShowAsync();
});

I thought I should wait for closure of the dialog, but by using Dispatcher I queue this dialog to the main UI thread, which handles this itself, or not? Thanks for any explanation of this problem.

Edit - I proceeded step by step and got following code, which is contained in same class. When I run app, LoadDataToModel is called. This is ok and dialog is shown by msgGPSDisabled. After that is event raised and locator_StatusChanged is called. This is ok too and dialog is shown. Now the strange part. When I do not call msgGPSDisabled in LoadDataToModel and only in the locator_StatusChanged, app crashes immediately after showing dialog. No exception and App.g.i.cs is opened on line 47 (DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION). Even if I use try-catch whereever it could be used. When I use msgGPSDisabled without Dispatcher in locator_StatusChanged, exceptions is raised. Not catchable, "item not found"

public async Task LoadDataToModel()
{
    await msgGPSDisabled();

    this.IsBusy = true;

    await LoadDataGarvis(Stations); //rozparsuje raw data a načte je do modelu
    InitializePins();

    this.IsBusy = false;


    }

void locator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
{
    switch (sender.LocationStatus)
    {
        case Windows.Devices.Geolocation.PositionStatus.Disabled:

            try
            {
                CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
                {
                    await msgGPSDisabled();
                    IsGPSBusy = false;
                    IsGPS = false;

                });
            }
            catch (UnauthorizedAccessException)
            {
                 throw;
            }
            catch (Exception) {throw; }

        case Windows.Devices.Geolocation.PositionStatus.NoData:
            CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                await msgGPSUnavailable();
            });

        }
    }


private async Task msgGPSDisabled()
{
    MessageDialog sss = new MessageDialog(_resourceLoader.GetString("MsgGPSDisabled"));
    await sss.ShowAsync();
}
like image 230
Qerts Avatar asked Mar 17 '23 16:03

Qerts


1 Answers

Two MessageDialogs can't be displayed at the same time. You have a few options if you want to continue using MessageDialogs and for all it would be best to have some sort of MessageDialogService to manage calls to bring up dialogs:

  • Close existing dialog when you need to open a new one. This is the simplest option and possibly the best, although you risk cancelling a dialog that might be somehow important depending on what your dialogs are about.
  • Queue up dialogs so the old ones don't get dismissed, but the new ones show up after the old ones were dismissed. This one will make sure all dialogs are closed by the user, but that could be a problem if your app can somehow start showing hundreds of dialogs.
  • Only open a new one if there isn't one already displayed. Now this risks that a newer message is not shown, which sounds more problematic than the first option.

If you'd like to go with the queue option - you could use this code:

using System;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Popups;
using Windows.UI.Xaml;

namespace WinRTXamlToolkit.Controls.Extensions
{
    /// <summary>
    /// MessageDialog extension methods
    /// </summary>
    public static class MessageDialogExtensions
    {
        private static TaskCompletionSource<MessageDialog> _currentDialogShowRequest;

        /// <summary>
        /// Begins an asynchronous operation showing a dialog.
        /// If another dialog is already shown using
        /// ShowAsyncQueue or ShowAsyncIfPossible method - it will wait
        /// for that previous dialog to be dismissed before showing the new one.
        /// </summary>
        /// <param name="dialog">The dialog.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException">This method can only be invoked from UI thread.</exception>
        public static async Task<IUICommand> ShowAsyncQueue(this MessageDialog dialog)
        {
            if (!Window.Current.Dispatcher.HasThreadAccess)
            {
                throw new InvalidOperationException("This method can only be invoked from UI thread.");
            }

            while (_currentDialogShowRequest != null)
            {
                await _currentDialogShowRequest.Task;
            }

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

            return result;
        }
    }
}
like image 165
Filip Skakun Avatar answered Mar 23 '23 19:03

Filip Skakun