Skip to content

Instantly share code, notes, and snippets.

@serbrech
Created August 31, 2011 12:30
Show Gist options
  • Save serbrech/1183423 to your computer and use it in GitHub Desktop.
Save serbrech/1183423 to your computer and use it in GitHub Desktop.
KeyGestureBehavior
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Input;
using PagePlanner.Windows.Controls.TriggerActions;
namespace PagePlanner.Windows.Controls.Behaviours.KeyGestures
{
public class KeyGestureCommandTrigger : KeyTrigger
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(KeyGestureCommandTrigger),
new PropertyMetadata(OnCommandChanged)
);
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
"CommandParameter",
typeof(object),
typeof(KeyGestureCommandTrigger),
null
);
public static readonly DependencyProperty GestureProperty = DependencyProperty.RegisterAttached(
"Gesture",
typeof(string),
typeof(KeyGestureCommandTrigger),
new PropertyMetadata(OnGestureChanged)
);
private static Regex gestureRx = new Regex(@"^\w+(\s*\+\s*\w+)*$", RegexOptions.IgnoreCase);
private static Dictionary<string, ModifierKeys> modifierMappings = new Dictionary<string, ModifierKeys>()
{
{ "ctrl", ModifierKeys.Control }
};
private static Dictionary<string, Key> keyMappings = new Dictionary<string, Key>()
{
{ "del", Key.Delete },
{ "ins", Key.Insert },
{ "esc", Key.Escape }
};
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var command = (ICommand)e.NewValue;
var actions = (System.Windows.Interactivity.TriggerActionCollection)d.GetValue(ActionsProperty);
var commandAction = (CommandTriggerAction)actions.FirstOrDefault(a => (a is CommandTriggerAction) && ((CommandTriggerAction)a).Command == command);
if (commandAction == null)
{
actions.Add(new CommandTriggerAction()
{
Command = GetCommand(d),
CommandParameter = GetCommandParameter(d)
});
}
}
/// <summary>
/// Here we are going to parse provided gesture into the set of modifier keys and the main key.
/// </summary>
private static void OnGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
string gesture = (string)e.NewValue;
ModifierKeys modifierKeys = ModifierKeys.None;
Key key = Key.None;
ParseGesture(gesture, out modifierKeys, out key);
d.SetValue(ModifiersProperty, modifierKeys);
d.SetValue(KeyProperty, key);
}
/// <summary>
/// Parses string gesture into the set of modifier keys and the main key.
/// </summary>
/// <param name="gesture">String gesture representation.</param>
/// <param name="modifierKeys">Modifier keys retuen value.</param>
/// <param name="key">Key return value.</param>
public static void ParseGesture(string gesture, out ModifierKeys modifierKeys, out Key key)
{
if (string.IsNullOrEmpty(gesture))
throw new ArgumentException("Gesture can not be empty string or null");
if (!gestureRx.IsMatch(gesture))
throw new ArgumentException("Wrong gesture format, should be something like 'Ctrl+E' or 'Ctrl+Shift+D'", "gesture");
modifierKeys = ModifierKeys.None;
key = Key.None;
var tokens = gesture.Split('+');
for (int i = 0; i < tokens.Length; i++)
{
var token = tokens[i].Trim().ToLower();
// We have modifier keys at the beginning.
if (i < tokens.Length - 1)
{
try
{
ModifierKeys modifierKey = (modifierMappings.ContainsKey(token)) ?
modifierMappings[token] : (ModifierKeys)Enum.Parse(typeof(ModifierKeys), token, true);
modifierKeys |= modifierKey;
}
catch (ArgumentException)
{
throw new FormatException(string.Format("Could not recognize {0} key", token));
}
}
// This is the main key.
else
{
try
{
key = (keyMappings.ContainsKey(token)) ?
keyMappings[token] : (Key)Enum.Parse(typeof(Key), token, true);
}
catch (ArgumentException)
{
throw new FormatException(string.Format("Could not recognize {0} key", token));
}
}
}
}
public static ICommand GetCommand(DependencyObject d)
{
return (ICommand)d.GetValue(CommandProperty);
}
public static object GetCommandParameter(DependencyObject d)
{
return d.GetValue(CommandParameterProperty);
}
public static string GetGesture(DependencyObject d)
{
return (string)d.GetValue(GestureProperty);
}
public static void SetCommand(DependencyObject d, ICommand command)
{
d.SetValue(CommandProperty, command);
}
public static void SetCommandParameter(DependencyObject d, object o)
{
d.SetValue(CommandParameterProperty, o);
}
public static void SetGesture(DependencyObject d, string gesture)
{
d.SetValue(GestureProperty, gesture);
}
}
}
/// <summary>
/// This is a similar KeyTrigger as the one in the Expression Library,
/// except that it attaches to the element where it is declared instead of
/// the Application Root.
/// </summary>
public class KeyTrigger : TriggerBase<UIElement>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register("Key", typeof(Key), typeof(KeyTrigger), null);
public static readonly DependencyProperty ModifiersProperty =
DependencyProperty.Register("Modifiers", typeof(ModifierKeys), typeof(KeyTrigger), null);
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
public ModifierKeys Modifiers
{
get { return (ModifierKeys)GetValue(ModifiersProperty); }
set { SetValue(ModifiersProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.KeyDown += AssociatedObject_KeyDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
}
private void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
{
if ((e.Key == this.Key) && (Keyboard.Modifiers == GetActualModifiers(e.Key, this.Modifiers)))
{
base.InvokeActions(e);
}
}
private static ModifierKeys GetActualModifiers(Key key, ModifierKeys modifiers)
{
if (key == Key.Ctrl)
{
modifiers |= ModifierKeys.Control;
return modifiers;
}
if (key == Key.Alt)
{
modifiers |= ModifierKeys.Alt;
return modifiers;
}
if (key == Key.Shift)
{
modifiers |= ModifierKeys.Shift;
}
return modifiers;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment