2011-01-06 37 views
16

Tôi có hộp thoại Có/Không từ UIAlertView với hai nút. Tôi muốn trong phương pháp của mình để thực hiện logic tương tự như sau:Messagebox.Show và DialogResult tương đương trong MonoTouch

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

Điều này là nếu tôi gọi UIAlertView.Show(), quá trình tiếp tục. Nhưng tôi cần phải chờ kết quả tương tác của người dùng và trả lại đúng hoặc sai khi nhấp vào nút thứ hai. Điều này có thể thực hiện được trong MonoTouch không?

Trả lời

18

Để thực hiện việc này, những gì bạn có thể làm là chạy thủ công mainloop. Tôi đã không quản lý để ngăn chặn các mainloop trực tiếp, vì vậy tôi thay vì chạy mainloop trong 0,5 giây và chờ cho đến khi người dùng phản ứng.

Chức năng sau đây cho thấy làm thế nào bạn có thể thực hiện một truy vấn phương thức với cách tiếp cận trên:

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; 
} 
+0

Một lần nữa, tôi đã tìm kiếm một cách để làm điều này từ rất lâu và có một vài câu trả lời "không thể làm" - và sau đó là Miguel! :-) – Krumelur

+0

Tôi có đúng khi nói rằng MonoDevelop hiện không xác định loại buttonArgs là UIButtonArgs trong trường hợp này (bên trong Lambda?) Tôi thấy rằng tôi chỉ muốn đảm bảo. (Để tự động hoàn thành. Không thấy nó hiển thị ButtonIndex, có vẻ như đang xử lý nó như một đối tượng.) –

+0

Tôi nhận được lỗi này khi thực hiện điều này trên MonoDevelop 3.1.1: [ERROR] LOẠI KHẨU BẤT HỢP CHẤT LƯỢNG KHÓA: MonoTouch.UIKit.UIKitThreadAccessException: UIKit Lỗi nhất quán: bạn đang gọi phương thức UIKit chỉ có thể được gọi từ chuỗi giao diện người dùng. –

0

MonoTouch (iOS) không có hộp thoại Modal, lý do là hộp thoại Modal (chờ) có thể gây ra các khóa chết nên các khung như Silverlight, Flex/Flash, iOS không cho phép các hộp thoại như vậy.

Cách duy nhất bạn có thể làm việc với nó là, bạn phải chuyển giao một đại biểu cho UIAlertView sẽ được gọi khi nó thành công. Tôi không biết cú pháp chính xác của UIAlertView nhưng bạn sẽ thấy tài liệu về UIAlertView, phải có một cách để vượt qua một lớp thực hiện giao thức/giao diện UIAlertViewDelegate. Điều đó sẽ có một phương thức sẽ được gọi khi hoàn thành hộp thoại.

+0

iOS không may cho phép hộp thoại modal: đó là những gì Apple sử dụng cho các cảnh báo Push Notification riêng của mình. – ptnik

+0

@ptnik có thể được Apple sử dụng nội bộ, nhưng nếu chúng cho phép các hộp thoại phương thức, nó sẽ tạo ra các vấn đề. –

17

Dựa trên của Miguel mã hóa, đây là một sự thay thế thuận tiện của MessageBox tiêu chuẩn:

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); 
     } 
    } 
} 
+0

đây là một triển khai rất tốt đẹp của MessageBox và giúp tôi tiết kiệm thời gian viết mã mọi lúc! – jharr100

1

tôi nghĩ rằng cách tiếp cận này sử dụng async/await tốt hơn nhiều và không bị đóng băng ứng dụng khi xoay thiết bị hoặc khi autoscrolling can thiệp và khiến bạn bị kẹt trong vòng lặp RunUntil mãi mãi mà không cần nhấp vào nút (ít nhất là các vấn đề dễ tái tạo trên 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; 
}  
0

Kết hợp danmiser và Ales' câu trả lời

  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); 
        } 
       } 
      } 
0

Đây là bản cập nhật khác, dựa trên sự đóng góp của Miguel, Ales, danmister và Patrick.

Kể từ khi phát hành iOS 11, đặc biệt là phiên bản 11.1.2 (lần đầu tiên tôi phát hiện ra phiên bản này), giải pháp ban đầu được đăng bởi tôi (Ales) trở nên không đáng tin cậy, bắt đầu đóng băng ngẫu nhiên. Điều này sử dụng một cách rõ ràng gọi NSRunLoop.Current.RunUntil(). Vì vậy, tôi đã cập nhật lớp gốc để thực sự cung cấp cả hai phương thức đồng bộ và không đồng bộ và đã thực hiện một số thay đổi khác để giải phóng bộ nhớ ngay lập tức sau khi nhấp vào bất kỳ nút nào. Ngắt dòng CRLF được phát hiện.

Namespaces:

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

Code:

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