Em WPF, uma Shape (retângulos, elipses, linhas,..) é um FrameworkElement e recebe os eventos de mouse e teclado, como qualquer outro FrameworkElement (botões, por exemplo). Isto é muito diferente de WinForms, onde as formas desenhadas usando GDI+ são apenas desenhos na tela e não recebem nenhum evento.
Podemos usar isso para movimentar nossas shapes com o mouse usando os eventos PreviewMouseLeftButtonDown, PreviewMouseLeftButtonUp e PreviewMouseMove.
Inicialmente, colocamos algumas shapes no Canvas:
<Canvas x:Name="canvas1"
PreviewMouseLeftButtonDown="canvas1_PreviewMouseDown"
PreviewMouseMove="canvas1_PreviewMouseMove"
PreviewMouseLeftButtonUp="canvas1_PreviewMouseUp">
<Rectangle Canvas.Top="10" Canvas.Left="10" Width="20" Height="40" Fill="Fuchsia" />
<Ellipse Canvas.Top="25" Canvas.Left="50" Width="40" Height="40" Fill="DarkSeaGreen" />
<Line Canvas.Top="120" Canvas.Left="50" X1="10" Y1="10" X2="50" Y2="50" Stroke="Navy"
StrokeThickness="3" />
<Polygon Canvas.Top="50" Canvas.Left="120" Points="30,20 80,24 80,54 30,20"
Fill="Red" />
</Canvas>
Como podemos ver, colocamos os manipuladores de eventos no Canvas. Devido ao recurso de Bubbling e Tunneling, os eventos são propagados por toda a árvore de elementos e, quando clicamos em qualquer uma das shapes, o Canvas recebe o evento também. Assim, podemos processar os eventos em um único local, não nos preocupando de atribuir os manipuladores para cada shape do desenho.
No código fonte, definimos alguns campos auxiliares:
Point start; // Ponto base para a movimentação
int currentZ; // Z-Index atual
bool isDragging; // Está movendo?
Shape movedElement; // Elemento sendo movido
O manipulador para o evento PreviewMouseLeftButtonDown é:
private void canvas1_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
// Verifica se clicamos numa Shape
if (e.Source is Shape)
{
// Pega posição atual do mouse
start = e.GetPosition(canvas1);
// Inicializa variáveis e configura opacidade da shape para 0.5
isDragging = true;
movedElement = (Shape)e.Source;
((Shape)e.Source).Opacity = 0.5;
canvas1.CaptureMouse();
e.Handled = true;
}
}
Aqui, inicializamos a variável start com a posição que o mouse foi clicado, configuramos isDragging para true, deixamos o elemento semi-transparente e capturamos o mouse. O manipulador para o evento PreviewMouseMove é:
private void canvas1_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
Point pt = e.GetPosition(canvas1);
// Pega posição atual da shape
double currentLeft = (double)movedElement.GetValue(Canvas.LeftProperty);
double currentTop = (double)movedElement.GetValue(Canvas.TopProperty);
// Calcula nova posição
double newLeft = currentLeft + pt.X - start.X;
double newTop = currentTop + pt.Y - start.Y;
// Reposiciona elemento
movedElement.SetValue(Canvas.LeftProperty, newLeft);
movedElement.SetValue(Canvas.TopProperty, newTop);
start = pt;
e.Handled = true;
}
}
Aqui verificamos se estamos arrastando um elemento. Se estivermos, pegamos a nova posição do mouse, recalculamos a posição da Shape e movemos. Como a posição é dada por attached properties, devemos usar SetValuee GetValue para obter e alterar as propriedades Canvas.Left e Canvas.Top. Finalmente, fechamos o movimento no evento PreviewMouseLeftButtonUp:
private void canvas1_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
// Restaura valores
movedElement.Opacity = 1;
movedElement.SetValue(Canvas.ZIndexProperty, ++currentZ);
isDragging = false;
canvas1.ReleaseMouseCapture();
}
Aqui apenas restauramos os valores. Uma coisa que deve ser notada é que estamos alterando a propriedade Canvas.ZIndex. Isto é devido ao fato que queremos que os elementos que movemos fiquem sempre por cima dos outros. Assim, usamos a variável currentZ para guardar o ZIndex, incrementando-o a cada movimento, de maneira que a cada vez que movemos um elemento, ele tenha um ZIndex maior que o anterior.
Desta maneira, podemos mover os elementos dentro de uma janela WPF. Note que este código pode ser estendido a qualquer FrameworkElement, não sendo apenas para Shapes.
O projeto completo está em https://github.com/bsonnino/ShapesMove
Hi … can we have access to the demo source project ??
thanks in advance
Sure. You can get it on http://www.revolution.com.br/dragshapes.zip