XAML:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10" Name="canvasControls"/>
</ScrollViewer>
These three handlers handle the dragging of controls around the canvas:
private void controls_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
position = e.GetPosition(canvasControls);
StackPanel panel = (StackPanel)sender;
if (editMode)
{
panel.CaptureMouse();
}
panel.Tag = true;
SetZIndex(panel);
}
private void controls_PreviewMouseMove(object sender, MouseEventArgs e)
{
StackPanel panel = (StackPanel)sender;
if (e.LeftButton == MouseButtonState.Pressed && editMode && (bool)panel.Tag)
{
Point currentPosition = e.GetPosition(canvasControls);
double moveX = currentPosition.X - position.X;
double moveY = currentPosition.Y - position.Y;
double newX = Canvas.GetLeft(panel) + moveX;
double newY = Canvas.GetTop(panel) + moveY;
FrameworkElement element = SnapToControl(panel, newX, true);
if (element != null)
{
newX = Canvas.GetLeft(element);
}
element = SnapToControl(panel, newY, false);
if (element != null)
{
newY = Canvas.GetTop(element);
}
Canvas.SetLeft(panel, newX);
Canvas.SetTop(panel, newY);
position = currentPosition;
}
}
private void controls_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
StackPanel panel = (StackPanel)sender;
panel.Tag = false;
panel.ReleaseMouseCapture();
RedrawCanvas(panel);
}
Because a canvas has no support for sizing to its children, of course there would be problems when moving around its children inside and outside of its boundaries. The following piece of code is called when you release the mouse on the control you're dragging and recalculates the canvas' dimensions. This way the scrollviewer around the canvas can display scrollbars when needed.
private void RedrawCanvas(FrameworkElement element)
{
double minTop = 0;
double minLeft = 0;
double maxTop = 0;
double maxLeft = 0;
foreach (FrameworkElement control in canvasControls.Children)
{
double top = Canvas.GetTop(control);
double left = Canvas.GetLeft(control);
if (top < minTop)
{
minTop = top;
}
if (top > maxTop)
{
maxTop = top;
}
if (left < minLeft)
{
minLeft = left;
}
if (left > maxLeft)
{
maxLeft = left;
}
}
if (minTop < 0)
{
foreach (FrameworkElement control in canvasControls.Children)
{
Canvas.SetTop(control, Canvas.GetTop(control) + Math.Abs(minTop));
}
}
if (minLeft < 0)
{
foreach (FrameworkElement control in canvasControls.Children)
{
Canvas.SetLeft(control, Canvas.GetLeft(control) + Math.Abs(minLeft));
}
}
canvasControls.Width = maxLeft + element.Width;
canvasControls.Height = maxTop + element.Height;
}
This method makes the control you're dragging the topmost one among the canvas' children so you're not dragging the control around below others.
private void SetZIndex(FrameworkElement control)
{
int z = 0;
foreach (FrameworkElement element in canvasControls.Children)
{
if (!element.Name.Equals(control.Name))
{
Canvas.SetZIndex(element, z++);
}
}
Canvas.SetZIndex(control, z);
}
Finally this method is used to provide snapping functionality to more easily align your controls on the canvas. It searches for controls positioned within the proximity of the control being moved. The snap distance determines how many pixels you must move your mouse cursor at once to 'snap out' of the hold again.
private FrameworkElement SnapToControl(FrameworkElement sender, double newPosition, bool x)
{
double snapDistance = Properties.Settings.Default.snap;
foreach (FrameworkElement element in canvasControls.Children)
{
if (!element.Name.Equals(sender.Name))
{
double difference = 0;
switch (x)
{
case true:
difference = Canvas.GetLeft(element) - newPosition; break;
case false:
difference = Canvas.GetTop(element) - newPosition; break;
}
difference = Math.Abs(difference);
if (difference <= snapDistance && difference > 0)
{
return element;
}
}
}
return null;
}
Geen opmerkingen:
Een reactie posten