/// <summary> /// Snap single element to close elements on canvas /// </summary> /// <param name="movedItem">Item to snap</param> /// <param name="canvas">Main canvas holding all items</param> private void SnapElement(ContentPresenter movedItem, CustomCanvas canvas) { double itemLeft = Canvas.GetLeft(movedItem); double itemTop = Canvas.GetTop(movedItem); TemplateViewModel context = (TemplateViewModel)canvas.DataContext; // get list of close items according to snapDeltaPixels value List <ContentPresenter> presenters = ControlHelper.GetUnselectedChildPresenters(canvas).ToList(); List <ContentPresenter> closeByLeft = presenters.Where(x => Math.Abs(Canvas.GetLeft(x) - itemLeft) <= snapDeltaPixels).ToList(); List <ContentPresenter> closeByTop = presenters.Where(x => Math.Abs(Canvas.GetTop(x) - itemTop) <= snapDeltaPixels).ToList(); List <Line> resultingSnapLines = new List <Line>(); if (closeByLeft.Count > 0) { // closest by left position and then by top ContentPresenter closest = closeByLeft.OrderBy(x => Math.Abs(Canvas.GetLeft(x) - itemLeft)) .ThenBy(x => Math.Abs(Canvas.GetTop(x) - itemTop)).First(); // move snapped item to desired position double targetLeft = Canvas.GetLeft(closest); double targetTop = Canvas.GetTop(closest); Canvas.SetLeft(movedItem, targetLeft); // add lowest item height to the line length double itemHeight = targetTop > itemTop ? closest.Height : movedItem.Height; // draw snap line and add to the list Line line = new Line(); line.X1 = line.X2 = targetLeft; line.Y1 = Math.Max(Math.Min(itemTop, targetTop) - snapLineTail, 0); line.Y2 = Math.Max(itemTop, targetTop) + itemHeight + snapLineTail; resultingSnapLines.Add(line); } if (closeByTop.Count > 0) { // closes by top position and then by left ContentPresenter closest = closeByTop.OrderBy(x => Math.Abs(Canvas.GetTop(x) - itemTop)) .ThenBy(x => Math.Abs(Canvas.GetLeft(x) - itemLeft)).First(); // move snapped item to desired position double targetLeft = Canvas.GetLeft(closest); double targetTop = Canvas.GetTop(closest); Canvas.SetTop(movedItem, targetTop); double itemWidth = targetLeft > itemLeft ? closest.Width : movedItem.Width; // draw snap line and add to the list Line line = new Line(); line.Y1 = line.Y2 = targetTop; line.X1 = Math.Max(Math.Min(itemLeft, targetLeft) - snapLineTail, 0); line.X2 = Math.Max(itemLeft, targetLeft) + itemWidth + snapLineTail; resultingSnapLines.Add(line); } if (context.GotSnapLines) { context.CleanSnapLines(); } // draw snap lines foreach (Line snapLine in resultingSnapLines) { context.AddLine(snapLine); } }
/// <summary> /// Snap group of elements /// </summary> /// <param name="movedGroup">Group of elements to snap</param> /// <param name="canvas">Main canvas holding all items</param> private void SnapGroup(List <ContentPresenter> movedGroup, CustomCanvas canvas) { // get bounding box Rect of snap group Rect boundingBox = GetBoundingBox(movedGroup); TemplateViewModel context = (TemplateViewModel)canvas.DataContext; List <ContentPresenter> canvasItems = ControlHelper.GetUnselectedChildPresenters(canvas).ToList(); // get list of close items according to snapDeltaPixels value List <Line> resultingSnapLines = new List <Line>(); List <ContentPresenter> closeByLeft = canvasItems.Where(x => Math.Abs(Canvas.GetLeft(x) - boundingBox.Left) <= snapDeltaPixels).ToList(); List <ContentPresenter> closeByTop = canvasItems.Where(x => Math.Abs(Canvas.GetTop(x) - boundingBox.Top) <= snapDeltaPixels).ToList(); if (closeByLeft.Count > 0) { // closes by left position and then by top ContentPresenter closest = closeByLeft.OrderBy(x => Math.Abs(Canvas.GetLeft(x) - boundingBox.Left)) .ThenBy(x => Math.Abs(Canvas.GetTop(x) - boundingBox.Top)).First(); double targetLeft = Canvas.GetLeft(closest); double targetTop = Canvas.GetTop(closest); // snap each item of the group according to calculated distance to the target double moveDelta = boundingBox.Left - targetLeft; movedGroup.ForEach(x => Canvas.SetLeft(x, Canvas.GetLeft(x) - moveDelta)); // add lowest item height to the line length double itemHeight = targetTop > boundingBox.Top ? closest.Height : boundingBox.Height; // draw snap line and add to the list Line line = new Line(); line.X1 = line.X2 = targetLeft; line.Y1 = Math.Max(Math.Min(boundingBox.Top, targetTop) - snapLineTail, 0); line.Y2 = Math.Max(boundingBox.Top, targetTop) + itemHeight + snapLineTail; resultingSnapLines.Add(line); } if (closeByTop.Count > 0) { // closes by top position and then by left ContentPresenter closest = closeByTop.OrderBy(x => Math.Abs(Canvas.GetTop(x) - boundingBox.Top)) .ThenBy(x => Math.Abs(Canvas.GetLeft(x) - boundingBox.Left)).First(); double targetLeft = Canvas.GetLeft(closest); double targetTop = Canvas.GetTop(closest); // snap each item of the group according to calculated distance to the target double moveDelta = boundingBox.Top - targetTop; movedGroup.ForEach(x => Canvas.SetTop(x, Canvas.GetTop(x) - moveDelta)); double itemWidth = targetLeft > boundingBox.Left ? closest.Width : boundingBox.Width; // draw snap line and add to the list Line line = new Line(); line.Y1 = line.Y2 = targetTop; line.X1 = Math.Max(Math.Min(boundingBox.Left, targetLeft) - snapLineTail, 0); line.X2 = Math.Max(boundingBox.Left, targetLeft) + itemWidth + snapLineTail; resultingSnapLines.Add(line); } if (context.GotSnapLines) { context.CleanSnapLines(); } // draw snap lines foreach (Line snapLine in resultingSnapLines) { context.AddLine(snapLine); } }