From 14a740dd5e705f3f5177bcc8c84b1ac51bf70362 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Mon, 20 Mar 2017 13:09:41 -0600 Subject: [PATCH] Make Entry completed behavior on UWP/WinRT match Android/iOS (#747) * Make Entry completed behavior on UWP/WinRT match Android/iOS * Accessibility test --- .../Bugzilla45067.cs | 76 ++++++++++++++++++++++ .../Xamarin.Forms.Controls.Issues.Shared.projitems | 1 + Xamarin.Forms.Platform.WinRT/EntryRenderer.cs | 10 +++ .../VisualElementRenderer.cs | 37 ++++++++++- 4 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla45067.cs diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla45067.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla45067.cs new file mode 100644 index 0000000..c4292fb --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla45067.cs @@ -0,0 +1,76 @@ +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Bugzilla, 45067, "[UWP] No way of cleanly dismissing soft keyboard", PlatformAffected.WinRT)] + public class Bugzilla45067 : TestContentPage + { + protected override void Init() + { + var button = new Button { Text = "Start" }; + + button.Clicked += (sender, args) => + { + SwitchMainPage(); + }; + + Content = button; + } + + void SwitchMainPage() + { + Application.Current.MainPage = new _45067Content(); + } + + class _45067Content : TestContentPage + { + protected override void Init() + { + var instructions1 = new Label { Text = "Enter text in the 'Username' Entry, then hit 'Enter/Return' on the soft keyboard. The keyboard should be dismissed." }; + + var instructions2 = new Label { Text = "Enter text in the 'Password' Entry, then hit 'Enter/Return' on the soft keyboard. The keyboard should be dismissed." }; + + var username = new Entry + { + Placeholder = "Username" + }; + + username.SetValue(Accessibility.LabeledByProperty, instructions1); + + var password = new Entry + { + Placeholder = "Password", + IsPassword = true + }; + + password.SetValue(Accessibility.LabeledByProperty, instructions2); + + var button = new Button { Text = "Submit", IsEnabled = false }; + + username.Completed += (s, e) => + { + button.Focus(); + }; + + password.Completed += (s, e) => + { + button.Focus(); + }; + + Content = new StackLayout + { + Children = + { + instructions1, + username, + instructions2, + password, + button + } + }; + } + } + } +} \ 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 d8b688d..1b060ef 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 @@ -165,6 +165,7 @@ + diff --git a/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs b/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs index a02b11c..06ec6f2 100644 --- a/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs +++ b/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs @@ -1,8 +1,10 @@ using System; using System.ComponentModel; +using System.Diagnostics; using System.Reflection; using Windows.Foundation.Metadata; using Windows.System; +using Windows.UI.Xaml; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Xamarin.Forms.Internals; @@ -111,6 +113,14 @@ namespace Xamarin.Forms.Platform.WinRT if (args?.Key != VirtualKey.Enter) return; +#if WINDOWS_UWP + // Hide the soft keyboard; this matches the behavior of Forms on Android/iOS + Windows.UI.ViewManagement.InputPane.GetForCurrentView().TryHide(); +#else + // WinRT doesn't have TryHide(), so the best we can do is force the control to unfocus + UnfocusControl(Control); +#endif + ((IEntryController)Element).SendCompleted(); } diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs index 9c3a4c9..e1bf2ef 100644 --- a/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs @@ -6,6 +6,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Automation; using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; #if WINDOWS_UWP @@ -25,6 +26,7 @@ namespace Xamarin.Forms.Platform.WinRT bool _disposed; EventHandler _elementChangedHandlers; VisualElementTracker _tracker; + Windows.UI.Xaml.Controls.Page _containingPage; // Cache of containing page used for unfocusing public TNativeElement Control { get; private set; } @@ -477,11 +479,40 @@ namespace Xamarin.Forms.Platform.WinRT internal void UnfocusControl(Control control) { - if (control == null || !control.IsEnabled) + if (control == null || !control.IsEnabled || !control.IsTabStop) return; - control.IsEnabled = false; - control.IsEnabled = true; + // "Unfocusing" doesn't really make sense on Windows; for accessibility reasons, + // something always has focus. So forcing the unfocusing of a control would normally + // just move focus to the next control, or leave it on the current control if no other + // focus targets are available. This is what happens if you use the "disable/enable" + // hack. What we *can* do is set the focus to the Page which contains Control; + // this will cause Control to lose focus without shifting focus to, say, the next Entry + + if (_containingPage == null) + { + // Work our way up the tree to find the containing Page + DependencyObject parent = Control as Control; + while (parent != null && !(parent is Windows.UI.Xaml.Controls.Page)) + { + parent = VisualTreeHelper.GetParent(parent); + } + _containingPage = parent as Windows.UI.Xaml.Controls.Page; + } + + if (_containingPage != null) + { + // Cache the tabstop setting + var wasTabStop = _containingPage.IsTabStop; + + // Controls can only get focus if they're a tabstop + _containingPage.IsTabStop = true; + _containingPage.Focus(FocusState.Programmatic); + + // Restore the tabstop setting; that may cause the Page to lose focus, + // but it won't restore the focus to Control + _containingPage.IsTabStop = wasTabStop; + } } void OnControlGotFocus(object sender, RoutedEventArgs args) -- 2.7.4