2011-01-06 12 views
16

Ich habe einen Ja/Nein-Dialog von UIAlertView mit zwei Tasten. Ich würde in meiner Methode mag die Logik ähnlich wie dies umzusetzen:MessageBox.Show und DialogResult Entsprechung in MonoTouch

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

Die Sache ist, wenn ich UIAlertView.Show() aufrufen, der Prozess wird fortgesetzt. Aber ich muss auf das Ergebnis der Benutzerinteraktion warten und true oder false depending beim Klicken auf die zweite Schaltfläche zurückgeben. Ist das in MonoTouch möglich?

Antwort

18

Um dies zu tun, können Sie den Haupttransport manuell ausführen. Ich habe es nicht geschafft, die Hauptschleife direkt anzuhalten, also lasse ich stattdessen die Hauptschleife für 0,5 Sekunden laufen und warte, bis der Benutzer antwortet.

Die folgende Funktion zeigt, wie Sie eine modale Abfrage mit dem obigen Ansatz implementieren könnten:

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

Da bin ich auf der Suche nach einem Weg, dies so lange zu tun und bekam ein paar "Nein kann" Antworten - und dann kommt Miguel! :-) – Krumelur

+0

Korrigiere ich, dass MonoDevelop den Typ buttonArgs in diesem Fall nicht als UIButtonArgs identifiziert (innerhalb des Lambda?), Sehe ich, dass ich nur sicher gehen wollte. (Für Auto-Vervollständigen. Es scheint nicht, es ButtonIndex zu sehen, es als ein Objekt zu behandeln.) –

+0

Ich habe diesen Fehler, wenn dies auf MonoDevelop 3.1.1: [FEHLER] FATAL UNHANDLED EXCEPTION: MonoTouch.UIKit.UIKitThreadAccessException: UIKit Konsistenzfehler: Sie rufen eine UIKit-Methode auf, die nur über den UInthread aufgerufen werden kann. –

0

MonoTouch (iOS) hat keine Modal-Dialoge, der Grund ist, dass Modal-Dialoge (Warten) Deadlocks verursachen können, so dass Frameworks wie Silverlight, Flex/Flash, iOS solche Dialoge nicht erlauben.

Die einzige Möglichkeit, wie Sie damit arbeiten können, ist, dass Sie einen Delegaten an UIAlertView übergeben müssen, der aufgerufen wird, wenn es erfolgreich war. Ich kenne nicht die genaue Syntax von UIAlertView, aber Sie sollten Dokumentation über UIAlertView sehen, es muss eine Möglichkeit geben, eine Klasse zu übergeben, die das UIAlertViewDelegate-Protokoll/Interface implementiert. Das wird eine Methode haben, die nach Abschluss des Dialogfeldes aufgerufen wird.

+0

iOS ermöglicht leider modale Dialoge: das ist, was Apple für seine eigene Push Notification Benachrichtigung verwendet. – ptnik

+0

@ptnik, die möglicherweise von Apple intern verwendet werden, aber wenn sie modale Dialoge zulassen, wird es Probleme verursachen. –

17

Basierend auf dem Miguels codierte, hier ist ein bequemer Austausch von Standard 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); 
     } 
    } 
} 
+0

das ist eine sehr nette Implementierung von MessageBox und spart mir Zeit, Code ständig zu schreiben! – jharr100

1

I Ich denke, dieser Ansatz mit async/await ist viel besser und leidet nicht unter dem Einfrieren der App beim Drehen des Geräts, oder wenn das automatische Scrollen stört und Sie für immer in der RunUntil-Schleife stecken bleiben, ohne die Möglichkeit zu haben, auf eine Schaltfläche zu klicken Probleme sind einfach auf iOS7 zu reproduzieren).

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

Kombinierte danmiser und Antworten 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); 
        } 
       } 
      } 
0

Hier ist ein weiteres Update, basierend auf Beiträgen von Miguel, Ales, danmister und Patrick.

Seit der Veröffentlichung von iOS 11, insbesondere Version 11.1.2 (ich habe es zuerst bemerkt), wurde die originale Lösung, wie sie von mir (Ales) gepostet wurde, unzuverlässig und begann willkürlich einzufrieren. Dieser verwendet explizit aufgerufene NSRunLoop.Current.RunUntil().

Also aktualisierte ich meine ursprüngliche Klasse, um sowohl sync als auch async Methoden anzubieten und machte einige andere Änderungen, um den Speicher sofort nach dem Klicken auf eine Schaltfläche freizugeben, fügte Code hinzu, der den Text nach links bei Windows ausrichtet CRLF-Zeilenumbrüche werden erkannt.

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