Last active
August 19, 2019 15:25
Declares WPF attached properties for focus handling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Declares common and useful attached properties. | |
/// </summary> | |
public static class AttachedProperties | |
{ | |
#region Focus Handling | |
// Define the key gesture type converter | |
[System.ComponentModel.TypeConverter(typeof(System.Windows.Input.KeyGestureConverter))] | |
public static KeyGesture GetFocusShortcut(DependencyObject dependencyObject) | |
{ | |
return (KeyGesture)dependencyObject?.GetValue(FocusShortcutProperty); | |
} | |
public static void SetFocusShortcut(DependencyObject dependencyObject, KeyGesture value) | |
{ | |
dependencyObject?.SetValue(FocusShortcutProperty, value); | |
} | |
/// <summary> | |
/// Enables window-wide focus shortcut for an <see cref="UIElement"/>. | |
/// </summary> | |
// Using a DependencyProperty as the backing store for FocusShortcut. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty FocusShortcutProperty = | |
DependencyProperty.RegisterAttached("FocusShortcut", typeof(KeyGesture), typeof(AttachedProperties), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnFocusShortcutChanged))); | |
private static void OnFocusShortcutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
if (!(d is UIElement element) || e.NewValue == e.OldValue) | |
return; | |
var window = FindParentWindow(d); | |
if (window == null) | |
return; | |
var gesture = GetFocusShortcut(d); | |
if (gesture == null) | |
{ | |
// Remove previous added input binding. | |
for (int i = 0; i < window.InputBindings.Count; i++) | |
{ | |
if (window.InputBindings[i].Gesture == e.OldValue && window.InputBindings[i].Command is FocusElementCommand) | |
window.InputBindings.RemoveAt(i--); | |
} | |
} | |
else | |
{ | |
// Add new input binding with the dedicated FocusElementCommand. | |
// see: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d | |
var command = new FocusElementCommand(element); | |
window.InputBindings.Add(new InputBinding(command, gesture)); | |
} | |
} | |
/// <summary> | |
/// Search recurse for the parent window containing the element. | |
/// <summary> | |
/// <param name="d"></param> | |
/// <returns>The parent window containing the element or null if not found.</returns> | |
private static Window FindParentWindow(DependencyObject d) | |
{ | |
if (d == null || d == DependencyProperty.UnsetValue) | |
return null; | |
if (d is Window window) | |
return window; | |
return FindParentWindow(VisualTreeHelper.GetParent(d)); | |
} | |
public static bool GetSelectAllOnFocus(DependencyObject dependencyObject) | |
{ | |
return (bool)dependencyObject?.GetValue(SelectAllOnFocusProperty); | |
} | |
public static void SetSelectAllOnFocus(DependencyObject dependencyObject, bool value) | |
{ | |
dependencyObject?.SetValue(SelectAllOnFocusProperty, value); | |
} | |
/// <summary> | |
/// Enables text boxes to auto select all content on focus. | |
/// </summary> | |
// Using a DependencyProperty as the backing store for SelectAllOnFocus. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty SelectAllOnFocusProperty = | |
DependencyProperty.RegisterAttached("SelectAllOnFocus", typeof(bool), typeof(AttachedProperties), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnSelectAllOnFocusChanged))); | |
private static void OnSelectAllOnFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
if (!(d is TextBoxBase textBox) || e.NewValue == e.OldValue) | |
return; | |
void focusHandler(object sender, EventArgs args) | |
{ | |
textBox.SelectAll(); | |
}; | |
if (GetSelectAllOnFocus(textBox)) | |
textBox.GotFocus += focusHandler; | |
else | |
textBox.GotFocus -= focusHandler; | |
} | |
public static bool GetAutoFocus(DependencyObject dependencyObject) | |
{ | |
return (bool)dependencyObject?.GetValue(AutoFocusProperty); | |
} | |
public static void SetAutoFocus(DependencyObject dependencyObject, bool value) | |
{ | |
dependencyObject?.SetValue(AutoFocusProperty, value); | |
} | |
/// <summary> | |
/// Enables controls to auto focus on load. | |
/// </summary> | |
// Using a DependencyProperty as the backing store for AutoFocus. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty AutoFocusProperty = | |
DependencyProperty.RegisterAttached("AutoFocus", typeof(bool), typeof(AttachedProperties), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnAutoFocusChanged))); | |
private static void OnAutoFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
if (!(d is FrameworkElement element) || e.NewValue == e.OldValue) | |
return; | |
void loadHandler(object sender, EventArgs args) | |
{ | |
if (element.Focusable && element.IsVisible && element.IsEnabled) | |
element.Focus(); | |
element.Loaded -= loadHandler; | |
}; | |
if (GetSelectAllOnFocus(element)) | |
element.Loaded += loadHandler; | |
else | |
element.Loaded -= loadHandler; | |
} | |
#endregion | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Implements a simple command that will focus an <see cref="UIElement"/> on execution. | |
/// </summary> | |
public sealed class FocusElementCommand : ICommand | |
{ | |
private readonly UIElement _element; | |
/// <summary> | |
/// Create a new instance of the command. | |
/// </summary> | |
/// <param name="element">The element to focus on execution.</param> | |
public FocusElementCommand(UIElement element) | |
{ | |
_element = element; | |
} | |
/// <summary> | |
/// <see cref="ICommand.CanExecuteChanged"/> | |
/// </summary> | |
public event EventHandler CanExecuteChanged; | |
/// <summary> | |
/// Checks if the command can be executed. This will verify if the element is valid, visible, enabled and focusable. | |
/// | |
/// <see cref="ICommand.CanExecute(object)"/> | |
/// </summary> | |
/// <param name="parameter">Command parameter is not used.</param> | |
/// <returns>True if the command can be executed.</returns> | |
public bool CanExecute(object parameter) | |
{ | |
if (_element == null) | |
return false; | |
if (!_element.IsEnabled || _element.IsVisible || !_element.Focusable) | |
return false; | |
return true; | |
} | |
/// <summary> | |
/// Set focus to the element. | |
/// | |
/// <see cref="ICommand.Execute(object)"/> | |
/// </summary> | |
/// <param name="parameter">Command parameter is not used.</param> | |
public void Execute(object parameter) | |
{ | |
_element.Focus(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment