I accepted @mdm20's solution, but my actual implementation is a little different. For one thing, I don't want to handle all KeyDown events (e.g. Tab shouldn't be handled). (Interestingly, I found that this method can be used to move the entire window.)
public CanvasControlArrowKeyTest()
{
InitializeComponent();
EventManager.RegisterClassHandler(typeof(Window), UIElement.KeyDownEvent, new KeyEventHandler(HandleKeyDown));
}
private void HandleKeyDown(object sender, KeyEventArgs e)
{
UIElement element = e.OriginalSource as UIElement;
if (element != null)
{
double left = Canvas.GetLeft(element);
if (Double.IsNaN(left)) left = 0;
double top = Canvas.GetTop(element);
if (Double.IsNaN(top)) top = 0;
switch (e.Key)
{
case Key.Left: left--; break;
case Key.Right: left++; break;
case Key.Up: top--; break;
case Key.Down: top++; break;
default: return;
}
if (left < 0) left = 0;
if (top < 0) top = 0;
Canvas.SetLeft(element, left);
Canvas.SetTop(element, top);
e.Handled = true;
}
}
For reference, here's the XAML:
<Window x:Class="DiagramDesigner.CanvasControlArrowKeyTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CanvasControlArrowKeyTest" Height="300" Width="300">
<Canvas>
<Canvas.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
<Setter Property="Background" Value="Black"/>
</Style>
</Canvas.Resources>
<Button />
<Button />
</Canvas>
</Window>