Messagebox.Show и DialogResult эквивалент в MonoTouch
У меня есть диалоговое окно "Да/Нет" из UIAlertView с двумя кнопками. Я хотел бы в моем методе реализовать логику, подобную этой:
if(messagebox.Show() == DialogResult.OK)
Дело в том, что я вызываю UIAlertView.Show(), процесс продолжается. Но мне нужно дождаться результата взаимодействия с пользователем и вернуть true или false depanding при нажатии второй кнопки. Возможно ли это в MonoTouch?
Ответы
Ответ 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;
}
}