Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@shuebner20
Last active August 19, 2019 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shuebner20/c6a5191be23da549d5004ee56bcc352d to your computer and use it in GitHub Desktop.
Save shuebner20/c6a5191be23da549d5004ee56bcc352d to your computer and use it in GitHub Desktop.
Declares WPF attached properties for focus handling
/// <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
}
/// <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