Making WPF controls double-clickable
A common UI pattern features the ability to double-click on a control to go in “edit” mode. So for example, you have a TextBlock that shows you the name of an object, and you can double-click it to change it into a TextBox where you can edit that name. At this point, it’s easy to hook up the MouseDoubleClick event, or some other mouse event, but that’s not very MVVM-like, is it?
Thankfully, WPF gives us the awesome attached dependency property API, which we already used for adding auto-complete to TextBoxes. The goal is to be able to write something like this:
<TextBlock Text="{Binding Name}" mi:ExtendedCommands.DoubleClickCommand="{Binding EditNameCommand}" mi:ExtendedCommands.DoubleClickCommandParameter="42" />
When the TextBlock would get double-clicked, the “EditNameCommand” command object on the DataContext would get called with “42” as the parameter. This is super easy to do:
public static class ExtendedCommands { public static readonly DependencyProperty DoubleClickCommandProperty; public static readonly DependencyProperty DoubleClickCommandParameterProperty; static ExtendedCommands() { DoubleClickCommandProperty = DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(ExtendedCommands), new UIPropertyMetadata(null, OnDoubleClickCommandPropertyChanged)); DoubleClickCommandParameterProperty = DependencyProperty.RegisterAttached("DoubleClickCommandParameter", typeof(object), typeof(ExtendedCommands), new UIPropertyMetadata(null)); } public static ICommand GetDoubleClickCommand(DependencyObject obj) { return (ICommand)obj.GetValue(DoubleClickCommandProperty); } public static void SetDoubleClickCommand(DependencyObject obj, ICommand value) { obj.SetValue(DoubleClickCommandProperty, value); } public static object GetDoubleClickCommandParameter(DependencyObject obj) { return (object)obj.GetValue(DoubleClickCommandParameterProperty); } public static void SetDoubleClickCommandParameter(DependencyObject obj, object value) { obj.SetValue(DoubleClickCommandParameterProperty, value); } private static void OnDoubleClickCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var element = d as UIElement; if (element != null) { if (e.OldValue == null && e.NewValue != null) { element.MouseDown += new MouseButtonEventHandler(Control_MouseDown); } else if (e.OldValue != null && e.NewValue == null) { element.MouseDown -= new MouseButtonEventHandler(Control_MouseDown); } } } private static void Control_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount == 2) { var element = sender as UIElement; if (element != null) { var command = GetDoubleClickCommand(element); var parameter = GetDoubleClickCommandParameter(element); if (command != null && command.CanExecute(parameter)) { e.Handled = true; command.Execute(parameter); } } } } }
This code is pretty naive, but it gets the job done and illustrates quickly how to do it: just register 2 attached dependency properties in a new custom class, and whenever someone uses it on a UI element, start listening to the “MouseDown” event on that element and check against the “ClickCount” property to figure out if it’s a double-click. If it is indeed a double-click, just run the command that was specified for this control!
Obviously, you’ll need to handle error cases a bit better (like for instance if these properties are used on a non-UIElement), make it a bit better (for example register the MouseDoubleClick event instead of the MouseDown event if the target object is a Control), and other stuff like that. But, well, by now, you should know not to copy/paste code found on blogs without using your brain anyway.