Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] Fix issue with caching the page that holds unfocused control #13028

Merged
merged 4 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Hosting;
using Xunit;

namespace Microsoft.Maui.DeviceTests
Expand Down Expand Up @@ -52,6 +55,67 @@ await InvokeOnMainThreadAsync(() =>
Assert.True(platformControl.IsReadOnly);
});
}


[Fact(DisplayName = "Unfocus will work when page is shown a 2nd time")]
public async Task UnFocusOnEntryAfterPagePop()
{
int unfocused = 0;
EnsureHandlerCreated(builder =>
{
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(Toolbar), typeof(ToolbarHandler));
handlers.AddHandler(typeof(NavigationPage), typeof(NavigationViewHandler));
handlers.AddHandler<Page, PageHandler>();
handlers.AddHandler(typeof(Window), typeof(WindowHandlerStub));
handlers.AddHandler(typeof(Entry), typeof(EntryHandler));

});
});
AutoResetEvent _focused = new AutoResetEvent(false);
AutoResetEvent _unFocused = new AutoResetEvent(false);
var entry = new Entry();
entry.Unfocused += (s, e) =>
{
if (!e.IsFocused)
{
unfocused++;
}
_unFocused.Set();
};
var navPage = new NavigationPage(new ContentPage { Content = entry });
var window = new Window(navPage);

await CreateHandlerAndAddToWindow<WindowHandlerStub>(window, async (handler) =>
{
await Task.Run(() =>
{
InvokeOnMainThreadAsync(() =>
{
entry.Focused += (s, e) => _focused.Set();
entry.Focus();
});
_focused.WaitOne();
_focused.Reset();
InvokeOnMainThreadAsync(async () =>
{
entry.Unfocus();
await navPage.PushAsync(new ContentPage());
await navPage.PopAsync();
entry.Focus();
});
_focused.WaitOne();
_unFocused.Reset();
InvokeOnMainThreadAsync(() =>
{
entry.Unfocus();
});
_unFocused.WaitOne();
Assert.True(unfocused == 2);
});
});
}
#endif

[Theory(DisplayName = "CursorPosition Initializes Correctly")]
Expand Down
40 changes: 5 additions & 35 deletions src/Core/src/Platform/Windows/ViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ namespace Microsoft.Maui.Platform
{
public static partial class ViewExtensions
{
internal static Page? ContainingPage; // Cache of containing page used for unfocusing

public static void TryMoveFocus(this FrameworkElement platformView, FocusNavigationDirection direction)
{
if (platformView?.XamlRoot?.Content is UIElement elem)
Expand Down Expand Up @@ -361,39 +359,11 @@ internal static void UnfocusControl(Control control)
if (control == null || !control.IsEnabled)
return;

// "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;

while (parent != null && parent is not Page)
{
parent = VisualTreeHelper.GetParent(parent);
}

ContainingPage = parent as 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;
}
var isTabStop = control.IsTabStop;
control.IsTabStop = false;
control.IsEnabled = false;
rachelkang marked this conversation as resolved.
Show resolved Hide resolved
control.IsEnabled = true;
control.IsTabStop = isTabStop;
}

internal static IWindow? GetHostedWindow(this IView? view)
Expand Down