“Striving for perfection” is probably the slogan when one of our project owners is writing requirements for our project. Over the past months we had to implement various UI enhancements which are not supported in WPF by default. Thanks to the flexibility of WPF, we managed to implement a lot of them. I will probably start sharing them here on my blog.
Let’s start with a simple one. The following “LeftCursorJustifiedComboBox” Attached Dependency Property sets the cursor at the beginning of the selected text. By default, the WPF ComboBox marks the whole text and sets the cursor at the end as shown below in the left picture.
What we were looking for is presented in the right screen shot.
By attaching the following Dependency Property to a ComboBox, the control starts behaving as shown on the right picture.
The code is searching within the Visual Tree of the ComoboBox the TextBox element and sets the position of the cursor whenever the dropdown gets closed.
public static class LeftCursorJustifiedComboBox { public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(ComboBox), new PropertyMetadata(OnChanged)); public static bool GetIsEnabled(DependencyObject obj) { return (bool)obj.GetValue(IsEnabledProperty); } public static void SetIsEnabled(DependencyObject obj, bool value) { obj.SetValue(IsEnabledProperty, value); } private static void OnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { SetIsEnabled(obj, (bool)args.NewValue); var box = (ComboBox)obj; box.DropDownClosed -= OnDropDownClosed; if (GetIsEnabled(obj)) { box.DropDownClosed += OnDropDownClosed; } } private static void OnDropDownClosed(object sender, EventArgs e) { var textBox = ((ComboBox)sender).GetChild<TextBox>(); textBox.CaretIndex = 0; } } public static class UIExtensions { public static TChild GetChild<TChild>(this DependencyObject element) where TChild : DependencyObject { if (element is TChild) { return (TChild)element; } for (int x = 0; x < VisualTreeHelper.GetChildrenCount(element); x++) { var child = VisualTreeHelper.GetChild(element, x); var childElement = child.GetChild<TChild>(); if (childElement != null) { return childElement; } } return null; } }
But how do I know that the child element I’m looking for is of type TextBox? Thanks to the new Live Visual Tree feature in Visual Studio 2015 it’s pretty easy to figure out!