简体   繁体   中英

Xamarin/Android - Make a modal window that waits like MessageBox.Show from C# .NET

I've been working on porting an application to Android from .NET WinForms for a client, and I have run into a snag with a requirement for Dialogs that wait for user interaction before continuing.

I'll say this now, up front. While I'm relatively new to Android, I am aware that this is frowned upon in Android because of impacts on the UI thread. I understand this, however, I've been told in no uncertain terms they want it the same way it works on .NET and I fail to see how async methods and callbacks will allow me to do that without massively re-organizing their routines (against, their wishes). So, for the purpose of this question, doing it the generally ideal way is not an option for me.

I have tried some different options to achieve the result I want, nothing has been 100%. I am close though with this:

public class MessageBox
{
    public class mHandler : Handler
    {
        public override void HandleMessage(Message mesg)
        {
            throw new MessageBoxException();
        }
    }

    public class MessageBoxException : Exception
    {
        public MessageBoxException()
        {
        }

        public MessageBoxException(string message)
            : base(message)
        {
        }

        public MessageBoxException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }

    public enum MessageBoxResult
    {
        Positive, Negative, Ignore, Cancel, Closed
    };

    private static Handler handler;
    private static MessageBoxResult yesNoDialogResult;

    public static MessageBoxResult Show(Context context, String title, String message, String positiveMessage, String negativeMessage)
    {
        yesNoDialogResult = MessageBoxResult.Closed;
        AlertDialog.Builder alert = new AlertDialog.Builder(context);
        alert.SetTitle(title);
        alert.SetMessage(message);
        alert.SetCancelable(false);
        alert.SetIcon(Android.Resource.Drawable.IcDialogAlert);

        handler = new mHandler();
        alert.SetPositiveButton("OK", (senderAlert, args) =>
        {
            yesNoDialogResult = MessageBoxResult.Positive;
            handler.SendMessage(handler.ObtainMessage());
        });

        alert.SetNegativeButton("Cancel", (senderAlert, args) =>
        {
            yesNoDialogResult = MessageBoxResult.Negative;
            handler.SendMessage(handler.ObtainMessage());
        });

        alert.Show(); 

        try { Looper.Loop(); }
        catch (MessageBoxException exc) { }

        return yesNoDialogResult;
    }
}

I have created a MessageBox class with a Show(). To make it wait, I am using a custom Handler and the main Looper to trigger a custom Exception, which I am then handling.

At runtime, this all works, quite well actually. The problem I'm having however is that when I debug my application using Visual Studio 2013, it always breaks when I throw my exception.

To combat this, I've tried changing my Exception settings to never break on my custom exception, to no avail. I've also tried the use of [System.Diagnostics.DebuggerHidden] to bypass debugging the error, but all that accomplished was moving the point where it fails from inside the Show or Handler to instead the .Show() method call when I tried to use it.

If anyone can point me in the right direction here for either a fix to this, or another workable solution, I would be extremely grateful.

Using EventWaitHandle to make a AlertDialog "modal":

public class MessageBox
{
    public enum MessageBoxResult
    {
        Positive, Negative, Ignore, Cancel, Closed
    };

    private static MessageBoxResult yesNoDialogResult;

    public static async Task<MessageBoxResult> Show(Context context, String title, String message, String positiveMessage, String negativeMessage)
    {
        yesNoDialogResult = MessageBoxResult.Closed;
        var alert = new AlertDialog.Builder(context)
           .SetTitle(title).SetMessage(message)
           .SetCancelable(false)
           .SetIcon(Android.Resource.Drawable.IcDialogAlert);

        var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

        alert.SetPositiveButton("OK", (senderAlert, args) =>
        {
            yesNoDialogResult = MessageBoxResult.Positive;
            waitHandle.Set();
        });

        alert.SetNegativeButton("Cancel", (senderAlert, args) =>
        {
            yesNoDialogResult = MessageBoxResult.Negative;
            waitHandle.Set();
        });

        alert.Show();
        await Task.Run(() => waitHandle.WaitOne());
        return yesNoDialogResult;
    }
}

Usage:

button.Click += async delegate { 
    Console.WriteLine("MessageBox.Show");
    var answer = await MessageBox.Show(this, "Stack", "Overflow", "", "");
    Console.WriteLine(answer);
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM