【问题标题】:Messagebox.Show and DialogResult equivalent in MonoTouchMonoTouch 中的 Messagebox.Show 和 DialogResult 等效项
【发布时间】:2011-06-04 12:37:50
【问题描述】:

我有一个来自 UIAlertView 的是/否对话框,带有两个按钮。我想在我的方法中实现类似这样的逻辑:

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

问题是,如果我调用 UIAlertView.Show(),过程会继续。但是我需要等待用户交互的结果并在单击第二个按钮时返回真或假。这在 MonoTouch 中可行吗?

【问题讨论】:

    标签: c# iphone ipad xamarin.ios


    【解决方案1】:

    MonoTouch (iOS) 没有 Modal 对话框,原因是 Modal 对话框(等待)会导致死锁,因此 Silverlight、Flex/Flash、iOS 等框架不允许此类对话框。

    您可以使用它的唯一方法是,您必须将委托传递给 UIAlertView,该委托将在成功时调用。我不知道 UIAlertView 的确切语法,但您应该查看有关 UIAlertView 的文档,必须有一种方法可以传递实现 UIAlertViewDelegate 协议/接口的类。这将有一个在对话框完成时调用的方法。

    【讨论】:

    • 不幸的是,iOS 允许模式对话框:这就是 Apple 用于自己的推送通知警报的方式。
    • @ptnik 可能被 Apple 内部使用,但如果他们允许模态对话框,则会产生问题。
    【解决方案2】:

    为此,您可以手动运行主循环。我没有设法直接停止主循环,所以我改为运行主循环 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;
    }
    

    【讨论】:

    • 一个,因为我一直在寻找一种方法来做到这一点,并得到了几个“不能做”的答案 - 然后是米格尔! :-)
    • 在这种情况下(在 Lambda 内部?)MonoDevelop 目前没有将类型 buttonArgs 识别为 UIButtonArgs 是否正确,我看到我只是想确定一下。 (用于自动完成。没有看到它显示 ButtonIndex,似乎将其视为对象。)
    • 在 MonoDevelop 3.1.1 上执行此操作时出现此错误:[错误] FATAL UNHANDLED EXCEPTION: MonoTouch.UIKit.UIKitThreadAccessException: UIKit 一致性错误:您正在调用只能从以下位置调用的 UIKit 方法UI 线程。
    • 使用async/await 比使用NSRunLoop 容易得多prashantvc.com/modal-uialertview-ios-7
    【解决方案3】:

    基于 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);
            }
        }
    }
    

    【讨论】:

    • 这是 MessageBox 的一个非常好的实现,并且一直节省我编写代码的时间!
    【解决方案4】:

    我认为这种使用 async/await 的方法要好得多,并且不会在旋转设备时冻结应用程序,或者当自动滚动干扰并使您永远卡在 RunUntil 循环中而无法单击按钮时(至少这些问题在 iOS7 上很容易重现)。

    Modal 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;
    }       
    

    【讨论】:

      【解决方案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】:

        这是另一个更新,基于 Miguel、Ales、danmister 和 Patrick 的贡献。

        自从 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;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-14
          • 1970-01-01
          相关资源
          最近更新 更多