From d0b90702aca68818f59ea03deb0a7acf45277f07 Mon Sep 17 00:00:00 2001 From: adrianknight89 Date: Mon, 15 Jul 2019 03:30:26 -0500 Subject: [PATCH] [Core] Add ability to show modal prompts (#6714) * display prompts * added keyboard * moved null arguments to Page * added test * changed property name * fix default literal issues * fixes --- .../Issue6713.cs | 46 +++++++++++++++++++++ .../Xamarin.Forms.Controls.Issues.Shared.projitems | 1 + Xamarin.Forms.Core/Page.cs | 9 +++++ Xamarin.Forms.Core/PromptArguments.cs | 42 +++++++++++++++++++ Xamarin.Forms.Platform.Android/PopupManager.cs | 47 +++++++++++++++++++++- Xamarin.Forms.Platform.iOS/Platform.cs | 28 +++++++++++++ 6 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue6713.cs create mode 100644 Xamarin.Forms.Core/PromptArguments.cs diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue6713.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue6713.cs new file mode 100644 index 0000000..6643c56 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue6713.cs @@ -0,0 +1,46 @@ +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 6713, "[Enhancement] Display prompts", PlatformAffected.iOS | PlatformAffected.Android)] + public class Issue6713 : TestContentPage // or TestMasterDetailPage, etc ... + { + protected override void Init() + { + var scrollView = new ScrollView(); + + var stackLayout = new StackLayout + { + Orientation = StackOrientation.Vertical, + Spacing = 10, + HorizontalOptions = LayoutOptions.FillAndExpand, + VerticalOptions = LayoutOptions.FillAndExpand + }; + + var button = new Button { Text = "Default keyboard" }; + button.Clicked += async (sender, e) => + { + var result = await DisplayPromptAsync("What’s the most useless product around today?", "The USB pet rock is definitely up there. What items do you have a hard time believing they actually exist?"); + + if (result != null) + (sender as Button).Text = result; + }; + stackLayout.Children.Add(button); + + var button2 = new Button { Text = "Numeric keyboard" }; + button2.Clicked += async (sender, e) => + { + var result = await DisplayPromptAsync("What’s the meaning of life?", "You know that number.", maxLength:2, keyboard:Keyboard.Numeric); + + if (result != null) + (sender as Button).Text = result; + }; + stackLayout.Children.Add(button2); + + scrollView.Content = stackLayout; + Content = scrollView; + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index b5bb847..668cc12 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -515,6 +515,7 @@ Issue5268.xaml Code + diff --git a/Xamarin.Forms.Core/Page.cs b/Xamarin.Forms.Core/Page.cs index ac0b784..bbf9d23 100644 --- a/Xamarin.Forms.Core/Page.cs +++ b/Xamarin.Forms.Core/Page.cs @@ -18,6 +18,8 @@ namespace Xamarin.Forms public const string AlertSignalName = "Xamarin.SendAlert"; + public const string PromptSignalName = "Xamarin.SendPrompt"; + public const string ActionSheetSignalName = "Xamarin.ShowActionSheet"; internal static readonly BindableProperty IgnoresContainerAreaProperty = BindableProperty.Create("IgnoresContainerArea", typeof(bool), typeof(Page), false); @@ -196,6 +198,13 @@ namespace Xamarin.Forms return args.Result.Task; } + public Task DisplayPromptAsync(string title, string message, string accept = "OK", string cancel = "Cancel", string placeholder = null, int maxLength = -1, Keyboard keyboard = default(Keyboard)) + { + var args = new PromptArguments(title, message, accept, cancel, placeholder, maxLength, keyboard); + MessagingCenter.Send(this, PromptSignalName, args); + return args.Result.Task; + } + public void ForceLayout() { SizeAllocated(Width, Height); diff --git a/Xamarin.Forms.Core/PromptArguments.cs b/Xamarin.Forms.Core/PromptArguments.cs new file mode 100644 index 0000000..5170119 --- /dev/null +++ b/Xamarin.Forms.Core/PromptArguments.cs @@ -0,0 +1,42 @@ +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Xamarin.Forms.Internals +{ + [EditorBrowsable(EditorBrowsableState.Never)] + public class PromptArguments + { + public PromptArguments(string title, string message, string accept = "OK", string cancel = "Cancel", string placeholder = null, int maxLength = -1, Keyboard keyboard = default(Keyboard)) + { + Title = title; + Message = message; + Accept = accept; + Cancel = cancel; + Placeholder = placeholder; + MaxLength = maxLength; + Keyboard = keyboard ?? Keyboard.Default; + Result = new TaskCompletionSource(); + } + + public string Title { get; } + + public string Message { get; } + + public string Accept { get; } + + public string Cancel { get; } + + public string Placeholder { get; } + + public int MaxLength { get; } + + public Keyboard Keyboard { get; } + + public TaskCompletionSource Result { get; } + + public void SetResult(string text) + { + Result.TrySetResult(text); + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/PopupManager.cs b/Xamarin.Forms.Platform.Android/PopupManager.cs index 458b6a6..9c8735d 100644 --- a/Xamarin.Forms.Platform.Android/PopupManager.cs +++ b/Xamarin.Forms.Platform.Android/PopupManager.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.Linq; using Android.App; using Android.Content; +using Android.Text; +using Android.Views; +using Android.Widget; using Xamarin.Forms.Internals; namespace Xamarin.Forms.Platform.Android @@ -46,6 +49,7 @@ namespace Xamarin.Forms.Platform.Android Activity = context; MessagingCenter.Subscribe(Activity, Page.BusySetSignalName, OnPageBusy); MessagingCenter.Subscribe(Activity, Page.AlertSignalName, OnAlertRequested); + MessagingCenter.Subscribe(Activity, Page.PromptSignalName, OnPromptRequested); MessagingCenter.Subscribe(Activity, Page.ActionSheetSignalName, OnActionSheetRequested); } @@ -53,8 +57,9 @@ namespace Xamarin.Forms.Platform.Android public void Dispose() { - MessagingCenter.Unsubscribe(Activity, Page.AlertSignalName); MessagingCenter.Unsubscribe(Activity, Page.BusySetSignalName); + MessagingCenter.Unsubscribe(Activity, Page.AlertSignalName); + MessagingCenter.Unsubscribe(Activity, Page.PromptSignalName); MessagingCenter.Unsubscribe(Activity, Page.ActionSheetSignalName); } @@ -122,6 +127,46 @@ namespace Xamarin.Forms.Platform.Android alert.Show(); } + void OnPromptRequested(Page sender, PromptArguments arguments) + { + // Verify that the page making the request is part of this activity + if (!PageIsInThisContext(sender)) + { + return; + } + + AlertDialog alertDialog = new AlertDialog.Builder(Activity).Create(); + alertDialog.SetTitle(arguments.Title); + alertDialog.SetMessage(arguments.Message); + + var frameLayout = new FrameLayout(Activity); + var editText = new EditText(Activity) { Hint = arguments.Placeholder }; + var layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent) + { + LeftMargin = (int)(22 * Activity.Resources.DisplayMetrics.Density), + RightMargin = (int)(22 * Activity.Resources.DisplayMetrics.Density) + }; + + editText.LayoutParameters = layoutParams; + editText.InputType = arguments.Keyboard.ToInputType(); + if (arguments.Keyboard == Keyboard.Numeric) + editText.KeyListener = LocalizedDigitsKeyListener.Create(editText.InputType); + + if (arguments.MaxLength > -1) + editText.SetFilters(new IInputFilter[]{ new InputFilterLengthFilter(arguments.MaxLength)}); + + frameLayout.AddView(editText); + alertDialog.SetView(frameLayout); + + alertDialog.SetButton((int)DialogButtonType.Positive, arguments.Accept, (o, args) => arguments.SetResult(editText.Text)); + alertDialog.SetButton((int)DialogButtonType.Negative, arguments.Cancel, (o, args) => arguments.SetResult(null)); + alertDialog.CancelEvent += (o, args) => { arguments.SetResult(null); }; + + alertDialog.Window.SetSoftInputMode(SoftInput.StateVisible); + alertDialog.Show(); + editText.RequestFocus(); + } + void UpdateProgressBarVisibility(bool isBusy) { if (!SupportsProgress) diff --git a/Xamarin.Forms.Platform.iOS/Platform.cs b/Xamarin.Forms.Platform.iOS/Platform.cs index d2021cc..21fb6e7 100644 --- a/Xamarin.Forms.Platform.iOS/Platform.cs +++ b/Xamarin.Forms.Platform.iOS/Platform.cs @@ -358,6 +358,26 @@ namespace Xamarin.Forms.Platform.iOS PresentPopUp(window, alert); } + void PresentPrompt(PromptArguments arguments) + { + var window = new UIWindow { BackgroundColor = Color.Transparent.ToUIColor() }; + + var alert = UIAlertController.Create(arguments.Title, arguments.Message, UIAlertControllerStyle.Alert); + alert.AddTextField(uiTextField => + { + uiTextField.Placeholder = arguments.Placeholder; + uiTextField.ShouldChangeCharacters = (field, range, replacementString) => arguments.MaxLength <= -1 || field.Text.Length + replacementString.Length - range.Length <= arguments.MaxLength; + uiTextField.ApplyKeyboard(arguments.Keyboard); + }); + var oldFrame = alert.View.Frame; + alert.View.Frame = new RectangleF(oldFrame.X, oldFrame.Y, oldFrame.Width, oldFrame.Height - _alertPadding * 2); + + alert.AddAction(CreateActionWithWindowHide(arguments.Cancel, UIAlertActionStyle.Cancel, () => arguments.SetResult(null), window)); + alert.AddAction(CreateActionWithWindowHide(arguments.Accept, UIAlertActionStyle.Default, () => arguments.SetResult(alert.TextFields[0].Text), window)); + + PresentPopUp(window, alert); + } + void PresentActionSheet(ActionSheetArguments arguments) { var alert = UIAlertController.Create(arguments.Title, null, UIAlertControllerStyle.ActionSheet); @@ -541,6 +561,13 @@ namespace Xamarin.Forms.Platform.iOS PresentAlert(arguments); }); + MessagingCenter.Subscribe(this, Page.PromptSignalName, (Page sender, PromptArguments arguments) => + { + if (!PageIsChildOfPlatform(sender)) + return; + PresentPrompt(arguments); + }); + MessagingCenter.Subscribe(this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments arguments) => { if (!PageIsChildOfPlatform(sender)) @@ -554,6 +581,7 @@ namespace Xamarin.Forms.Platform.iOS { MessagingCenter.Unsubscribe(this, Page.ActionSheetSignalName); MessagingCenter.Unsubscribe(this, Page.AlertSignalName); + MessagingCenter.Unsubscribe(this, Page.PromptSignalName); MessagingCenter.Unsubscribe(this, Page.BusySetSignalName); } -- 2.7.4