Подтвердить что ты не робот

Messagebox.Show и DialogResult эквивалент в MonoTouch

У меня есть диалоговое окно "Да/Нет" из UIAlertView с двумя кнопками. Я хотел бы в моем методе реализовать логику, подобную этой:

if(messagebox.Show() == DialogResult.OK)

Дело в том, что я вызываю UIAlertView.Show(), процесс продолжается. Но мне нужно дождаться результата взаимодействия с пользователем и вернуть true или false depanding при нажатии второй кнопки. Возможно ли это в MonoTouch?

4b9b3361

Ответ 1

Чтобы сделать это, вы можете просто запустить mainloop вручную. Мне не удалось остановить mainloop напрямую, поэтому вместо этого я запустил mainloop на 0,5 секунды и дождитесь ответа пользователя.

Следующая функция показывает, как вы могли бы реализовать модальный запрос с помощью вышеупомянутого подхода:

int WaitForClick ()
{
    int clicked = -1;
    var x = new UIAlertView ("Title", "Message",  null, "Cancel", "OK", "Perhaps");
    x.Show ();
    bool done = false;
    x.Clicked += (sender, buttonArgs) => {
        Console.WriteLine ("User clicked on {0}", buttonArgs.ButtonIndex);
    clicked = buttonArgs.ButtonIndex;
    };    
    while (clicked == -1){
        NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.5));
        Console.WriteLine ("Waiting for another 0.5 seconds");
    }

    Console.WriteLine ("The user clicked {0}", clicked);
    return clicked;
}

Ответ 2

Основанный на кодировке Miguel, здесь удобно заменить стандартный MessageBox:

using System;
using System.Drawing;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using System.Collections.Generic;

namespace YourNameSpace
{

    public enum MessageBoxResult
    {
        None = 0,
        OK,
        Cancel,
        Yes,
        No
    }

    public enum MessageBoxButton
    {
        OK = 0,
        OKCancel,
        YesNo,
        YesNoCancel
    }

    public static class MessageBox
    {
        public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton buttonType)
        {
            MessageBoxResult res = MessageBoxResult.Cancel;
            bool IsDisplayed = false;
            int buttonClicked = -1;
            MessageBoxButton button = buttonType;
            UIAlertView alert = null;

            string cancelButton = "Cancel";
            string[] otherButtons = null;

            switch (button)
            {
                case MessageBoxButton.OK:
                    cancelButton = "";
                    otherButtons = new string[1];
                    otherButtons[0] = "OK";
                    break;

                case MessageBoxButton.OKCancel:
                    otherButtons = new string[1];
                    otherButtons[0] = "OK";
                    break;

                case MessageBoxButton.YesNo:
                    cancelButton = "";
                    otherButtons = new string[2];
                    otherButtons[0] = "Yes";
                    otherButtons[1] = "No";
                    break;

                case MessageBoxButton.YesNoCancel:
                    otherButtons = new string[2];
                    otherButtons[0] = "Yes";
                    otherButtons[1] = "No";
                    break;
            }

            if (cancelButton.Length > 0)
                alert = new UIAlertView(caption, messageBoxText, null, cancelButton, otherButtons);
            else
                alert = new UIAlertView(caption, messageBoxText, null, null, otherButtons);

            alert.BackgroundColor = UIColor.FromWhiteAlpha(0f, 0.8f);
            alert.Canceled += (sender, e) => {
                buttonClicked = 0;
                IsDisplayed = false;
            };

            alert.Clicked += (sender, e) => {
                buttonClicked = e.ButtonIndex;
                IsDisplayed = false;
            };

            alert.Dismissed += (sender, e) => {
                if (IsDisplayed)
                {
                    buttonClicked = e.ButtonIndex;
                    IsDisplayed = false;
                }
            };

            alert.Show();

            IsDisplayed = true;

            while (IsDisplayed)
            {
                NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.2));
            }

            switch (button)
            {
                case MessageBoxButton.OK:
                    res = MessageBoxResult.OK;
                    break;

                case MessageBoxButton.OKCancel:
                    if (buttonClicked == 1)
                        res = MessageBoxResult.OK;
                    break;

                case MessageBoxButton.YesNo:
                    if (buttonClicked == 0)
                        res = MessageBoxResult.Yes;
                    else
                        res = MessageBoxResult.No;
                    break;

                case MessageBoxButton.YesNoCancel:
                    if (buttonClicked == 1)
                        res = MessageBoxResult.Yes;
                    else if (buttonClicked == 2)
                        res = MessageBoxResult.No;
                    break;
            }

            return res;
        }

        public static MessageBoxResult Show(string messageBoxText)
        {
            return Show(messageBoxText, "", MessageBoxButton.OK);
        }

        public static MessageBoxResult Show(string messageBoxText, string caption)
        {
            return Show(messageBoxText, caption, MessageBoxButton.OK);
        }
    }
}

Ответ 3

Я думаю, что этот подход с использованием async/await намного лучше и не страдает от замораживания приложения при вращении устройства или когда автопрокрутка мешает и оставляет вас застрявшим в цикле RunUntil навсегда без возможности щелчка кнопки (по крайней мере эти проблемы легко воспроизвести на iOS7).

Модальный UIAlertView

Task<int> ShowModalAletViewAsync (string title, string message, params string[] buttons)
{
    var alertView = new UIAlertView (title, message,  null, null, buttons);
    alertView.Show ();
    var tsc = new TaskCompletionSource<int> ();

    alertView.Clicked += (sender, buttonArgs) => {
        Console.WriteLine ("User clicked on {0}", buttonArgs.ButtonIndex);      
        tsc.TrySetResult(buttonArgs.ButtonIndex);
    };    
    return tsc.Task;
}       

Ответ 4

MonoTouch (iOS) не имеет модальных диалогов, причина в том, что модальные диалоги (ожидание) могут вызвать взаимоблокировки, поэтому такие среды, как Silverlight, Flex/Flash, iOS, не позволяют такие диалоги.

Единственный способ, с которым вы можете работать, - передать делегат в UIAlertView, который будет вызываться, когда он будет успешным. Я не знаю точного синтаксиса UIAlertView, но вы должны увидеть документацию о UIAlertView, должен быть способ передать класс, реализующий протокол/интерфейс UIAlertViewDelegate. Это будет иметь метод, который будет вызываться при завершении диалогового окна.

Ответ 5

Комбинированные ответы danmiser и Ales

            using System;
            using System.Drawing;
            using MonoTouch.UIKit;
            using MonoTouch.Foundation;
            using System.Collections.Generic;
            using System.Threading.Tasks;

            namespace yournamespace
            {

                public enum MessageBoxResult
                {
                    None = 0,
                    OK,
                    Cancel,
                    Yes,
                    No
                }

                public enum MessageBoxButton
                {
                    OK = 0,
                    OKCancel,
                    YesNo,
                    YesNoCancel
                }

                public static class MessageBox
                {
                    public static Task<MessageBoxResult> ShowAsync(string messageBoxText, string caption, MessageBoxButton buttonType)
                    {
                        MessageBoxResult res = MessageBoxResult.Cancel;
                        bool IsDisplayed = false;
                        int buttonClicked = -1;
                        MessageBoxButton button = buttonType;
                        UIAlertView alert = null;

                        string cancelButton = "Cancel";
                        string[] otherButtons = null;

                        switch (button)
                        {
                        case MessageBoxButton.OK:
                            cancelButton = "";
                            otherButtons = new string[1];
                            otherButtons[0] = "OK";
                            break;

                        case MessageBoxButton.OKCancel:
                            otherButtons = new string[1];
                            otherButtons[0] = "OK";
                            break;

                        case MessageBoxButton.YesNo:
                            cancelButton = "";
                            otherButtons = new string[2];
                            otherButtons[0] = "Yes";
                            otherButtons[1] = "No";
                            break;

                        case MessageBoxButton.YesNoCancel:
                            otherButtons = new string[2];
                            otherButtons[0] = "Yes";
                            otherButtons[1] = "No";
                            break;
                        }

                        var tsc = new TaskCompletionSource<MessageBoxResult> ();

                        if (cancelButton.Length > 0)
                            alert = new UIAlertView(caption, messageBoxText, null, cancelButton, otherButtons);
                        else
                            alert = new UIAlertView(caption, messageBoxText, null, null, otherButtons);

                        alert.BackgroundColor = UIColor.FromWhiteAlpha(0f, 0.8f);
                        alert.Canceled += (sender, e) => {
                            tsc.TrySetResult( MessageBoxResult.Cancel);
                        };

                        alert.Clicked += (sender, e) => {
                            buttonClicked = e.ButtonIndex;
                            switch (button)
                            {
                            case MessageBoxButton.OK:
                                res = MessageBoxResult.OK;
                                break;

                            case MessageBoxButton.OKCancel:
                                if (buttonClicked == 1)
                                    res = MessageBoxResult.OK;
                                break;

                            case MessageBoxButton.YesNo:
                                if (buttonClicked == 0)
                                    res = MessageBoxResult.Yes;
                                else
                                    res = MessageBoxResult.No;
                                break;

                            case MessageBoxButton.YesNoCancel:
                                if (buttonClicked == 1)
                                    res = MessageBoxResult.Yes;
                                else if (buttonClicked == 2)
                                    res = MessageBoxResult.No;
                                break;
                            }
                            tsc.TrySetResult( res);
                        };

                        alert.Show();

                        return tsc.Task;
                    }

                    public static Task<MessageBoxResult> ShowAsync(string messageBoxText)
                    {
                        return ShowAsync(messageBoxText, "", MessageBoxButton.OK);
                    }

                    public static Task<MessageBoxResult> ShowAsync(string messageBoxText, string caption)
                    {
                        return ShowAsync(messageBoxText, caption, MessageBoxButton.OK);
                    }
                }
            }

Ответ 6

Вот еще одно обновление, основанное на материалах Мигеля, Алеса, Данмистера и Патрика.

С момента выпуска iOS 11, в частности версии 11.1.2 (я впервые заметил это на этом), оригинальное решение, опубликованное мной (Ales), стало ненадежным, начало беспорядочно замораживаться. В этом используется явно вызванный NSRunLoop.Current.RunUntil().

Итак, я обновил свой первоначальный класс, чтобы на самом деле предложить как методы синхронизации, так и асинхронные действия, и сделал несколько других изменений, чтобы освободить память сразу после нажатия любой кнопки, а также добавил код, который выравнивает текст влево, если Windows CRLF разрывы строк.

Пространство имен:

using System;
using CoreGraphics;
using UIKit;
using Foundation;
using System.Collections.Generic;
using System.Threading.Tasks;

код:

public enum MessageBoxResult
{
    None = 0,
    OK,
    Cancel,
    Yes,
    No
}

public enum MessageBoxButton
{
    OK = 0,
    OKCancel,
    YesNo,
    YesNoCancel
}

public static class MessageBox
{
    /* This class emulates Windows style modal boxes. Unfortunately, the original code doesn't work reliably since cca iOS 11.1.2 so 
     * you have to use the asynchronous methods provided here.
     * 
     * The code was a bit restructured utilising class MessageBoxNonstatic to make sure that on repeated use, it doesn't allocate momere memory.
     * Note that event handlers are explicitly removed and at the end I explicitly call garbage collector.
     * 
     * The code is a bit verbose to make it easier to understand and open it to tweaks.
     * 
    */


    // Synchronous methods - don't work well since iOS 11.1.2, often freeze because something has changed in the event loop and
    // NSRunLoop.Current.RunUntil() is not reliable to use anymore
    public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton buttonType)
    {
        MessageBoxNonstatic box = new MessageBoxNonstatic();
        return box.Show(messageBoxText, caption, buttonType);
    }

    public static MessageBoxResult Show(string messageBoxText)
    {
        return Show(messageBoxText, "", MessageBoxButton.OK);
    }

    public static MessageBoxResult Show(string messageBoxText, string caption)
    {
        return Show(messageBoxText, caption, MessageBoxButton.OK);
    }

    // Asynchronous methods - use with await keyword. Restructure the calling code tho accomodate async calling patterns
    // See https://docs.microsoft.com/en-us/dotnet/csharp/async
    /*
     async void DecideOnQuestion()
     {
         if (await MessageBox.ShowAsync("Proceed?", "DECIDE!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
         {
             // Do something
         }
     }
     */
    public static Task<MessageBoxResult> ShowAsync(string messageBoxText, string caption, MessageBoxButton buttonType)
    {
        MessageBoxNonstatic box = new MessageBoxNonstatic();
        return box.ShowAsync(messageBoxText, caption, buttonType);
    }

    public static Task<MessageBoxResult> ShowAsync(string messageBoxText)
    {
        return ShowAsync(messageBoxText, "", MessageBoxButton.OK);
    }

    public static Task<MessageBoxResult> ShowAsync(string messageBoxText, string caption)
    {
        return ShowAsync(messageBoxText, caption, MessageBoxButton.OK);
    }
}

public class MessageBoxNonstatic
{
    private bool IsDisplayed = false;
    private int buttonClicked = -1;
    private UIAlertView alert = null;

    private string messageBoxText = "";
    private string caption = "";
    private MessageBoxButton button = MessageBoxButton.OK;

    public bool IsAsync = false;
    TaskCompletionSource<MessageBoxResult> tsc = null;

    public MessageBoxNonstatic()
    {
        // Do nothing
    }

    public MessageBoxResult Show(string sMessageBoxText, string sCaption, MessageBoxButton eButtonType)
    {
        messageBoxText = sMessageBoxText;
        caption = sCaption;
        button = eButtonType;
        IsAsync = false;

        ShowAlertBox();
        WaitInLoopWhileDisplayed();
        return GetResult();
    }

    public Task<MessageBoxResult> ShowAsync(string sMessageBoxText, string sCaption, MessageBoxButton eButtonType)
    {
        messageBoxText = sMessageBoxText;
        caption = sCaption;
        button = eButtonType;
        IsAsync = true;

        tsc = new TaskCompletionSource<MessageBoxResult>();
        ShowAlertBox();
        return tsc.Task;
    }

    private void ShowAlertBox()
    {
        IsDisplayed = false;
        buttonClicked = -1;
        alert = null;

        string cancelButton = "Cancel";
        string[] otherButtons = null;

        switch (button)
        {
            case MessageBoxButton.OK:
                cancelButton = "";
                otherButtons = new string[1];
                otherButtons[0] = "OK";
                break;

            case MessageBoxButton.OKCancel:
                otherButtons = new string[1];
                otherButtons[0] = "OK";
                break;

            case MessageBoxButton.YesNo:
                cancelButton = "";
                otherButtons = new string[2];
                otherButtons[0] = "Yes";
                otherButtons[1] = "No";
                break;

            case MessageBoxButton.YesNoCancel:
                otherButtons = new string[2];
                otherButtons[0] = "Yes";
                otherButtons[1] = "No";
                break;
        }

        IUIAlertViewDelegate d = null;
        if (cancelButton.Length > 0)
            alert = new UIAlertView(caption, messageBoxText, d, cancelButton, otherButtons);
        else
            alert = new UIAlertView(caption, messageBoxText, d, null, otherButtons);

        if (messageBoxText.Contains("\r\n"))
        {
            foreach (UIView v in alert.Subviews)
            {
                try
                {
                    UILabel l = (UILabel)v;
                    if (l.Text == messageBoxText)
                    {
                        l.TextAlignment = UITextAlignment.Left;
                    }
                }
                catch
                {
                    // Do nothing
                }
            }
        }

        alert.BackgroundColor = UIColor.FromWhiteAlpha(0f, 0.8f);
        alert.Canceled += Canceled_Click;
        alert.Clicked += Clicked_Click;
        alert.Dismissed += Dismissed_Click;

        alert.Show();

        IsDisplayed = true;
    }

    // ======================================================================= Private methods ==========================================================================

    private void WaitInLoopWhileDisplayed()
    {
        while (IsDisplayed)
        {
            NSRunLoop.Current.RunUntil(NSDate.FromTimeIntervalSinceNow(0.2));
        }
    }

    private void Canceled_Click(object sender, EventArgs e)
    {
        buttonClicked = 0;
        IsDisplayed = false;
        DisposeAlert();
    }

    private void Clicked_Click(object sender, UIButtonEventArgs e)
    {
        buttonClicked = (int)e.ButtonIndex;
        IsDisplayed = false;
        DisposeAlert();
    }

    private void Dismissed_Click(object sender, UIButtonEventArgs e)
    {
        if (IsDisplayed)
        {
            buttonClicked = (int)e.ButtonIndex;
            IsDisplayed = false;
            DisposeAlert();
        }
    }

    private void DisposeAlert()
    {
        alert.Canceled -= Canceled_Click;
        alert.Clicked -= Clicked_Click;
        alert.Dismissed -= Dismissed_Click;
        alert.Dispose();
        alert = null;
        GC.Collect();

        if (IsAsync)
            GetResult();
    }

    private MessageBoxResult GetResult()
    {
        MessageBoxResult res = MessageBoxResult.Cancel;

        switch (button)
        {
            case MessageBoxButton.OK:
                res = MessageBoxResult.OK;
                break;

            case MessageBoxButton.OKCancel:
                if (buttonClicked == 1)
                    res = MessageBoxResult.OK;
                break;

            case MessageBoxButton.YesNo:
                if (buttonClicked == 0)
                    res = MessageBoxResult.Yes;
                else
                    res = MessageBoxResult.No;
                break;

            case MessageBoxButton.YesNoCancel:
                if (buttonClicked == 1)
                    res = MessageBoxResult.Yes;
                else if (buttonClicked == 2)
                    res = MessageBoxResult.No;
                break;
        }

        if (IsAsync)
            tsc.TrySetResult(res);

        return res;
    }
}