tags:

views:

35

answers:

3

I have a number of controls inside a Canvas element and I want to be able to move them inside the Canvas using the arrow/directional keys (up, down, left, right). What's the easiest way to do this in WPF/code-behind? Is there an idiomatic WPF way of doing this?

I guess I should clarify: I want to be able to move each control independently; I do not want to move all of the controls at the same time.

+1  A: 

I was able to do it like this:

UPDATED: Now only moves the one with the focus (and stops the window from processing the event further).

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    int deltaX = 0, deltaY = 0;

    if (e.Key == Key.Left)
    {
        deltaX = -1;
    }
    else if (e.Key == Key.Right)
    {
        deltaX = 1;
    }
    else if (e.Key == Key.Up)
    {
        deltaY = -1;
    }
    else if (e.Key == Key.Down)
    {
        deltaY = 1;
    }

    foreach (UIElement element in this.canvas1.Children)
    {
        if (element.IsFocused)
        {
            double left = (double)element.GetValue(Canvas.LeftProperty);
            element.SetValue(Canvas.LeftProperty, left + deltaX);

            double top = (double)element.GetValue(Canvas.TopProperty);
            element.SetValue(Canvas.TopProperty, top + deltaY);                                             
        }            
    }
    e.Handled = true;
}
steinar
Updated my question: I guess I should clarify: I want to be able to move each control independently; I do not want to move all of the controls at the same time. Therefore, I need to know which of the controls to move (i.e. which one has focus).
Pat
Updated. Is this what you're looking for?
steinar
Yes, I think that would work. I didn't use it, though, because @mdm20's solution doesn't rely on iterating. Also, I would replace the foreach in your answer with a LINQ statement `.Where(e => e.IsFocused)`.
Pat
+1  A: 

Here is my solution:

First, capture key down events

EventManager.RegisterClassHandler(typeof(MainWindow), UIElement.KeyDownEvent, new KeyEventHandler(KeyDownHandler));

Then figure out who sent the event and move it accordingly.

void KeyDownHandler(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;
        }

         Canvas.SetLeft(element, left);
         Canvas.SetTop(element, top);
    }
    e.Handled = true;
}
mdm20
A: 

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>
Pat