-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Fix dialog losing focus when changing WindowState #3923
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
8822070
2dc7672
31fec8b
a0c440c
3c6ea51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,8 @@ public class DialogHost : ContentControl | |
private DialogClosingEventHandler? _attachedDialogClosingEventHandler; | ||
private DialogClosedEventHandler? _attachedDialogClosedEventHandler; | ||
private IInputElement? _restoreFocusDialogClose; | ||
private IInputElement? _lastFocusedDialogElement; | ||
private WindowState _previousWindowState; | ||
private Action? _currentSnackbarMessageQueueUnPauseAction; | ||
|
||
static DialogHost() | ||
|
@@ -370,6 +372,7 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj | |
|
||
dialogHost.CurrentSession = new DialogSession(dialogHost); | ||
var window = Window.GetWindow(dialogHost); | ||
dialogHost.ListenForWindowStateChanged(window); | ||
if (!dialogHost.IsRestoreFocusDisabled) | ||
{ | ||
dialogHost._restoreFocusDialogClose = window != null ? FocusManager.GetFocusedElement(window) : null; | ||
|
@@ -395,7 +398,8 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj | |
|
||
//https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/187 | ||
//totally not happy about this, but on immediate validation we can get some weird looking stuff...give WPF a kick to refresh... | ||
Task.Delay(300).ContinueWith(t => dialogHost.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { | ||
Task.Delay(300).ContinueWith(t => dialogHost.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => | ||
{ | ||
CommandManager.InvalidateRequerySuggested(); | ||
//Delay focusing the popup until after the animation has some time, Issue #2912 | ||
UIElement? child = dialogHost.FocusPopup(); | ||
|
@@ -405,6 +409,50 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj | |
}))); | ||
} | ||
|
||
|
||
private void ListenForWindowStateChanged(Window? window) | ||
{ | ||
window ??= Window.GetWindow(this); | ||
|
||
if (window is not null) | ||
{ | ||
window.StateChanged += Window_StateChanged; | ||
corvinsz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
private void Window_StateChanged(object? sender, EventArgs e) | ||
{ | ||
if (sender is not Window window) | ||
{ | ||
return; | ||
} | ||
|
||
var windowState = window.WindowState; | ||
if (windowState == WindowState.Minimized) | ||
{ | ||
_lastFocusedDialogElement = FocusManager.GetFocusedElement(window); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw your comment about this line in your past twitch broadcast (I think you forgot to submit it?): I honestly didn't think of that edge case. _lastFocusedDialogElement = FocusManager.GetFocusedElement(this);
I may be biased, but I don't like the current default style for the dialoghost anyway (that could be a me-problem though). |
||
_previousWindowState = windowState; | ||
return; | ||
} | ||
|
||
// We only need to focus anything manually if the window changes state from Minimized --> (Normal or Maximized) | ||
// Going from Normal --> Maximized (and vice versa) is fine since the focus is already kept correctly | ||
if (IsWindowRestoredFromMinimized() && IsLastFocusedDialogElementFocusable()) | ||
{ | ||
// Kinda hacky, but without a delay the focus doesn't always get set correctly because the Focus() method fires too early | ||
Task.Delay(50).ContinueWith(_ => this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => | ||
{ | ||
_lastFocusedDialogElement!.Focus(); | ||
}))); | ||
} | ||
corvinsz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
_previousWindowState = windowState; | ||
|
||
bool IsWindowRestoredFromMinimized() => (windowState == WindowState.Normal || windowState == WindowState.Maximized) && | ||
_previousWindowState == WindowState.Minimized; | ||
|
||
bool IsLastFocusedDialogElementFocusable() => _lastFocusedDialogElement is UIElement { Focusable: true, IsVisible: true }; | ||
} | ||
|
||
/// <summary> | ||
/// Returns a DialogSession for the currently open dialog for managing it programmatically. If no dialog is open, CurrentSession will return null | ||
/// </summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<UserControl x:Class="MaterialDesignThemes.UITests.Samples.DialogHost.WithMultipleTextBoxes" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:local="clr-namespace:MaterialDesignThemes.UITests.Samples.DialogHost" | ||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
d:DesignHeight="450" | ||
d:DesignWidth="800" | ||
mc:Ignorable="d"> | ||
<materialDesign:DialogHost x:Name="SampleDialogHost" Loaded="DialogHost_Loaded"> | ||
<materialDesign:DialogHost.DialogContent> | ||
<StackPanel Margin="16" | ||
HorizontalAlignment="Center" | ||
VerticalAlignment="Center"> | ||
<TextBox x:Name="TextBoxOne" | ||
MinWidth="200" | ||
Text="One" /> | ||
<TextBox x:Name="TextBoxTwo" | ||
MinWidth="200" | ||
Text="Two" /> | ||
<TextBox x:Name="TextBoxThree" | ||
MinWidth="200" | ||
Text="Three" /> | ||
</StackPanel> | ||
</materialDesign:DialogHost.DialogContent> | ||
</materialDesign:DialogHost> | ||
</UserControl> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
namespace MaterialDesignThemes.UITests.Samples.DialogHost; | ||
|
||
/// <summary> | ||
/// Interaction logic for WithMultipleTextBoxes.xaml | ||
/// </summary> | ||
public partial class WithMultipleTextBoxes : UserControl | ||
{ | ||
public WithMultipleTextBoxes() | ||
{ | ||
InitializeComponent(); | ||
} | ||
private void DialogHost_Loaded(object sender, RoutedEventArgs e) | ||
{ | ||
SampleDialogHost.IsOpen = true; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.