using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; using Ink_Canvas.Helpers; using iNKORE.UI.WPF.Modern.Controls; using Point = System.Windows.Point; namespace Ink_Canvas { public partial class MainWindow : Window { #region Floating Control private object lastBorderMouseDownObject; private void Border_MouseDown(object sender, MouseButtonEventArgs e) { // 如果发送者是 RandomDrawPanel 或 SingleDrawPanel,且它们被隐藏,则不处理事件 if (sender is SimpleStackPanel panel) { if ((panel == RandomDrawPanel || panel == SingleDrawPanel) && panel.Visibility != Visibility.Visible) { return; } } lastBorderMouseDownObject = sender; } private bool isStrokeSelectionCloneOn; private void BorderStrokeSelectionClone_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; if (isStrokeSelectionCloneOn) { BorderStrokeSelectionClone.Background = Brushes.Transparent; isStrokeSelectionCloneOn = false; } else { BorderStrokeSelectionClone.Background = new SolidColorBrush(StringToColor("#FF1ED760")); isStrokeSelectionCloneOn = true; } } private void BorderStrokeSelectionCloneToNewBoard_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; var strokes = inkCanvas.GetSelectedStrokes(); inkCanvas.Select(new StrokeCollection()); strokes = strokes.Clone(); BtnWhiteBoardAdd_Click(null, null); inkCanvas.Strokes.Add(strokes); } private void BorderStrokeSelectionDelete_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; SymbolIconDelete_MouseUp(sender, e); } private void GridPenWidthDecrease_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; ChangeStrokeThickness(0.8); } private void GridPenWidthIncrease_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; ChangeStrokeThickness(1.25); } private void ChangeStrokeThickness(double multipler) { foreach (var stroke in inkCanvas.GetSelectedStrokes()) { var newWidth = stroke.DrawingAttributes.Width * multipler; var newHeight = stroke.DrawingAttributes.Height * multipler; if (!(newWidth >= DrawingAttributes.MinWidth) || !(newWidth <= DrawingAttributes.MaxWidth) || !(newHeight >= DrawingAttributes.MinHeight) || !(newHeight <= DrawingAttributes.MaxHeight)) continue; stroke.DrawingAttributes.Width = newWidth; stroke.DrawingAttributes.Height = newHeight; } if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } private void GridPenWidthRestore_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; foreach (var stroke in inkCanvas.GetSelectedStrokes()) { stroke.DrawingAttributes.Width = inkCanvas.DefaultDrawingAttributes.Width; stroke.DrawingAttributes.Height = inkCanvas.DefaultDrawingAttributes.Height; } } private void ImageFlipHorizontal_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; var m = new Matrix(); // Find center of element and then transform to get current location of center var fe = e.Source as FrameworkElement; var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2); center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 // Update matrix to reflect translation/rotation m.ScaleAt(-1, 1, center.X, center.Y); // 缩放 var targetStrokes = inkCanvas.GetSelectedStrokes(); foreach (var stroke in targetStrokes) stroke.Transform(m, false); if (DrawingAttributesHistory.Count > 0) { //var collecion = new StrokeCollection(); //foreach (var item in DrawingAttributesHistory) //{ // collecion.Add(item.Key); //} timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } //updateBorderStrokeSelectionControlLocation(); } private void ImageFlipVertical_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; var m = new Matrix(); // Find center of element and then transform to get current location of center var fe = e.Source as FrameworkElement; var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2); center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 // Update matrix to reflect translation/rotation m.ScaleAt(1, -1, center.X, center.Y); // 缩放 var targetStrokes = inkCanvas.GetSelectedStrokes(); foreach (var stroke in targetStrokes) stroke.Transform(m, false); if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } // ... existing code ... private void ImageRotate45_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; var m = new Matrix(); // Find center of element and then transform to get current location of center var fe = e.Source as FrameworkElement; var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2); center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 // Update matrix to reflect translation/rotation m.RotateAt(45, center.X, center.Y); // 顺时针旋转45度 var targetStrokes = inkCanvas.GetSelectedStrokes(); foreach (var stroke in targetStrokes) stroke.Transform(m, false); if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } private void ImageRotate90_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; var m = new Matrix(); // Find center of element and then transform to get current location of center var fe = e.Source as FrameworkElement; var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2); center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 // Update matrix to reflect translation/rotation m.RotateAt(90, center.X, center.Y); // 旋转 var targetStrokes = inkCanvas.GetSelectedStrokes(); foreach (var stroke in targetStrokes) stroke.Transform(m, false); if (DrawingAttributesHistory.Count > 0) { var collecion = new StrokeCollection(); foreach (var item in DrawingAttributesHistory) { collecion.Add(item.Key); } timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } #endregion private bool isGridInkCanvasSelectionCoverMouseDown; private StrokeCollection StrokesSelectionClone = new StrokeCollection(); private void GridInkCanvasSelectionCover_MouseDown(object sender, MouseButtonEventArgs e) { isGridInkCanvasSelectionCoverMouseDown = true; } private void GridInkCanvasSelectionCover_MouseUp(object sender, MouseButtonEventArgs e) { if (!isGridInkCanvasSelectionCoverMouseDown) return; isGridInkCanvasSelectionCoverMouseDown = false; GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; } private void BtnSelect_Click(object sender, RoutedEventArgs e) { ExitMultiTouchModeIfNeeded(); forceEraser = true; drawingShapeMode = 0; inkCanvas.IsManipulationEnabled = false; if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) { if (inkCanvas.GetSelectedStrokes().Count == inkCanvas.Strokes.Count) { inkCanvas.EditingMode = InkCanvasEditingMode.Ink; inkCanvas.EditingMode = InkCanvasEditingMode.Select; } else { var selectedStrokes = new StrokeCollection(); foreach (var stroke in inkCanvas.Strokes) if (stroke.GetBounds().Width > 0 && stroke.GetBounds().Height > 0) selectedStrokes.Add(stroke); inkCanvas.Select(selectedStrokes); } } else { inkCanvas.EditingMode = InkCanvasEditingMode.Select; } } private double BorderStrokeSelectionControlWidth = 490.0; private double BorderStrokeSelectionControlHeight = 80.0; private bool isProgramChangeStrokeSelection; private void inkCanvas_SelectionChanged(object sender, EventArgs e) { if (isProgramChangeStrokeSelection) return; if (inkCanvas.GetSelectedStrokes().Count == 0) { GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; // 当没有选中笔画时,检查是否有选中的UIElement CheckUIElementSelection(); } else { GridInkCanvasSelectionCover.Visibility = Visibility.Visible; BorderStrokeSelectionClone.Background = Brushes.Transparent; isStrokeSelectionCloneOn = false; updateBorderStrokeSelectionControlLocation(); // 当选中笔画时,取消UIElement选择 DeselectUIElement(); } } private void CheckUIElementSelection() { // 检查InkCanvas中的UIElement是否被选中 var selectedElements = inkCanvas.GetSelectedElements(); if (selectedElements.Count > 0) { var element = selectedElements[0]; SelectUIElement(element); } else { DeselectUIElement(); } } private void updateBorderStrokeSelectionControlLocation() { var borderLeft = (inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Right - BorderStrokeSelectionControlWidth) / 2; var borderTop = inkCanvas.GetSelectionBounds().Bottom + 1; if (borderLeft < 0) borderLeft = 0; if (borderTop < 0) borderTop = 0; if (Width - borderLeft < BorderStrokeSelectionControlWidth || double.IsNaN(borderLeft)) borderLeft = Width - BorderStrokeSelectionControlWidth; if (Height - borderTop < BorderStrokeSelectionControlHeight || double.IsNaN(borderTop)) borderTop = Height - BorderStrokeSelectionControlHeight; if (borderTop > 60) borderTop -= 60; BorderStrokeSelectionControl.Margin = new Thickness(borderLeft, borderTop, 0, 0); } private void GridInkCanvasSelectionCover_ManipulationStarting(object sender, ManipulationStartingEventArgs e) { e.Mode = ManipulationModes.All; } private void GridInkCanvasSelectionCover_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { if (StrokeManipulationHistory?.Count > 0) { timeMachine.CommitStrokeManipulationHistory(StrokeManipulationHistory); foreach (var item in StrokeManipulationHistory) { StrokeInitialHistory[item.Key] = item.Value.Item2; } StrokeManipulationHistory = null; } if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } private void GridInkCanvasSelectionCover_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { try { if (dec.Count >= 1) { bool disableScale = dec.Count >= 3; var md = e.DeltaManipulation; var trans = md.Translation; // 获得位移矢量 var rotate = md.Rotation; // 获得旋转角度 var scale = md.Scale; // 获得缩放倍数 var m = new Matrix(); // Find center of element and then transform to get current location of center var fe = e.Source as FrameworkElement; var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2); center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 // Update matrix to reflect translation/rotation m.Translate(trans.X, trans.Y); // 移动 if (!disableScale) m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放 var strokes = inkCanvas.GetSelectedStrokes(); if (StrokesSelectionClone.Count != 0) strokes = StrokesSelectionClone; else if (Settings.Gesture.IsEnableTwoFingerRotationOnSelection) m.RotateAt(rotate, center.X, center.Y); // 旋转 foreach (var stroke in strokes) { stroke.Transform(m, false); try { stroke.DrawingAttributes.Width *= md.Scale.X; stroke.DrawingAttributes.Height *= md.Scale.Y; } catch { } } updateBorderStrokeSelectionControlLocation(); } } catch { } } private void GridInkCanvasSelectionCover_TouchDown(object sender, TouchEventArgs e) { } private void GridInkCanvasSelectionCover_TouchUp(object sender, TouchEventArgs e) { } private Point lastTouchPointOnGridInkCanvasCover = new Point(0, 0); private void GridInkCanvasSelectionCover_PreviewTouchDown(object sender, TouchEventArgs e) { dec.Add(e.TouchDevice.Id); //设备1个的时候,记录中心点 if (dec.Count == 1) { var touchPoint = e.GetTouchPoint(null); centerPoint = touchPoint.Position; lastTouchPointOnGridInkCanvasCover = touchPoint.Position; if (isStrokeSelectionCloneOn) { var strokes = inkCanvas.GetSelectedStrokes(); isProgramChangeStrokeSelection = true; inkCanvas.Select(new StrokeCollection()); StrokesSelectionClone = strokes.Clone(); inkCanvas.Select(strokes); isProgramChangeStrokeSelection = false; inkCanvas.Strokes.Add(StrokesSelectionClone); } else { // 新增:启动套索选择模式 inkCanvas.EditingMode = InkCanvasEditingMode.Select; inkCanvas.Select(new StrokeCollection()); } } } private void GridInkCanvasSelectionCover_PreviewTouchUp(object sender, TouchEventArgs e) { dec.Remove(e.TouchDevice.Id); if (dec.Count >= 1) return; isProgramChangeStrokeSelection = false; if (lastTouchPointOnGridInkCanvasCover == e.GetTouchPoint(null).Position) { if (!(lastTouchPointOnGridInkCanvasCover.X < inkCanvas.GetSelectionBounds().Left) && !(lastTouchPointOnGridInkCanvasCover.Y < inkCanvas.GetSelectionBounds().Top) && !(lastTouchPointOnGridInkCanvasCover.X > inkCanvas.GetSelectionBounds().Right) && !(lastTouchPointOnGridInkCanvasCover.Y > inkCanvas.GetSelectionBounds().Bottom)) return; inkCanvas.Select(new StrokeCollection()); StrokesSelectionClone = new StrokeCollection(); } else if (inkCanvas.GetSelectedStrokes().Count == 0) { GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; StrokesSelectionClone = new StrokeCollection(); } else { GridInkCanvasSelectionCover.Visibility = Visibility.Visible; StrokesSelectionClone = new StrokeCollection(); } } private void LassoSelect_Click(object sender, RoutedEventArgs e) { ExitMultiTouchModeIfNeeded(); forceEraser = false; forcePointEraser = false; drawingShapeMode = 0; inkCanvas.EditingMode = InkCanvasEditingMode.Select; SetCursorBasedOnEditingMode(inkCanvas); } private void BtnLassoSelect_Click(object sender, RoutedEventArgs e) { ExitMultiTouchModeIfNeeded(); forceEraser = false; forcePointEraser = false; drawingShapeMode = 0; inkCanvas.EditingMode = InkCanvasEditingMode.Select; inkCanvas.IsManipulationEnabled = true; SetCursorBasedOnEditingMode(inkCanvas); } #region UIElement Selection and Resize private UIElement selectedUIElement; private System.Windows.Controls.Canvas resizeHandlesCanvas; private readonly List resizeHandles = new List(); private bool isResizing; private ResizeDirection currentResizeDirection = ResizeDirection.None; private Point resizeStartPoint; private Rect originalElementBounds; // 图片工具栏相关 private Border borderImageSelectionControl; private double BorderImageSelectionControlWidth = 490.0; // 6个按钮 + 分隔线的实际宽度 private double BorderImageSelectionControlHeight = 80.0; // 元素变化监听相关 private DispatcherTimer elementUpdateTimer; private Rect lastElementBounds; private enum ResizeDirection { None, TopLeft, TopCenter, TopRight, MiddleLeft, MiddleRight, BottomLeft, BottomCenter, BottomRight } private void InitializeUIElementSelection() { // 创建拖拽手柄画布 if (resizeHandlesCanvas == null) { resizeHandlesCanvas = new System.Windows.Controls.Canvas { Background = Brushes.Transparent, IsHitTestVisible = true, Visibility = Visibility.Collapsed }; // 将手柄画布添加到主网格中,确保它在InkCanvas之上 var mainGrid = inkCanvas.Parent as Grid; if (mainGrid != null) { mainGrid.Children.Add(resizeHandlesCanvas); Panel.SetZIndex(resizeHandlesCanvas, 1000); // 确保在最上层 } } // 初始化图片工具栏引用 if (borderImageSelectionControl == null) { borderImageSelectionControl = FindName("BorderImageSelectionControl") as Border; } // 创建8个拖拽手柄 CreateResizeHandles(); } private void CreateResizeHandles() { resizeHandles.Clear(); resizeHandlesCanvas.Children.Clear(); var directions = new[] { ResizeDirection.TopLeft, ResizeDirection.TopCenter, ResizeDirection.TopRight, ResizeDirection.MiddleLeft, ResizeDirection.MiddleRight, ResizeDirection.BottomLeft, ResizeDirection.BottomCenter, ResizeDirection.BottomRight }; foreach (var direction in directions) { var handle = new Rectangle { Width = 12, Height = 12, Fill = Brushes.White, Stroke = Brushes.DodgerBlue, StrokeThickness = 2, Cursor = GetCursorForDirection(direction), Tag = direction }; handle.MouseDown += ResizeHandle_MouseDown; handle.MouseMove += ResizeHandle_MouseMove; handle.MouseUp += ResizeHandle_MouseUp; resizeHandles.Add(handle); resizeHandlesCanvas.Children.Add(handle); } } private Cursor GetCursorForDirection(ResizeDirection direction) { switch (direction) { case ResizeDirection.TopLeft: case ResizeDirection.BottomRight: return Cursors.SizeNWSE; case ResizeDirection.TopRight: case ResizeDirection.BottomLeft: return Cursors.SizeNESW; case ResizeDirection.TopCenter: case ResizeDirection.BottomCenter: return Cursors.SizeNS; case ResizeDirection.MiddleLeft: case ResizeDirection.MiddleRight: return Cursors.SizeWE; default: return Cursors.Arrow; } } private void SelectUIElement(UIElement element) { if (selectedUIElement == element) return; // 取消之前的选择 DeselectUIElement(); // 清除笔画选择 if (inkCanvas.GetSelectedStrokes().Count > 0) { isProgramChangeStrokeSelection = true; inkCanvas.Select(new StrokeCollection()); isProgramChangeStrokeSelection = false; } selectedUIElement = element; if (element != null) { // 初始化选择系统(如果还没有初始化) if (resizeHandlesCanvas == null) { InitializeUIElementSelection(); } // 显示拖拽手柄(所有UI元素都需要) ShowResizeHandles(); // 根据元素类型显示特定的工具栏 if (element is Image) { ShowImageToolbar(); } // 监听元素的布局变化,以便实时更新手柄位置 StartMonitoringElementChanges(element); } } private void DeselectUIElement() { // 停止监听之前选中元素的变化 StopMonitoringElementChanges(); selectedUIElement = null; HideResizeHandles(); HideImageToolbar(); } private void ShowResizeHandles() { if (selectedUIElement == null || resizeHandlesCanvas == null) return; var bounds = GetUIElementBounds(selectedUIElement); UpdateResizeHandlesPosition(bounds); resizeHandlesCanvas.Visibility = Visibility.Visible; } private void HideResizeHandles() { if (resizeHandlesCanvas != null) { resizeHandlesCanvas.Visibility = Visibility.Collapsed; } } private void ShowImageToolbar() { if (selectedUIElement == null || borderImageSelectionControl == null) return; var bounds = GetUIElementBounds(selectedUIElement); UpdateImageToolbarPosition(bounds); borderImageSelectionControl.Visibility = Visibility.Visible; } private void HideImageToolbar() { if (borderImageSelectionControl != null) { borderImageSelectionControl.Visibility = Visibility.Collapsed; } } private void UpdateImageToolbarPosition(Rect bounds) { if (borderImageSelectionControl == null) return; // 计算工具栏位置,类似于墨迹选择工具栏的逻辑 var toolbarX = bounds.X + bounds.Width / 2 - BorderImageSelectionControlWidth / 2; var toolbarY = bounds.Y + bounds.Height + 10; // 在图片下方10像素处 // 确保工具栏不会超出画布边界 if (toolbarX < 0) toolbarX = 0; if (toolbarX + BorderImageSelectionControlWidth > inkCanvas.ActualWidth) toolbarX = inkCanvas.ActualWidth - BorderImageSelectionControlWidth; if (toolbarY + BorderImageSelectionControlHeight > inkCanvas.ActualHeight) toolbarY = bounds.Y - BorderImageSelectionControlHeight - 10; // 如果下方空间不够,显示在上方 borderImageSelectionControl.Margin = new Thickness(toolbarX, toolbarY, 0, 0); } private Rect GetUIElementBounds(UIElement element) { if (element is FrameworkElement fe) { var left = InkCanvas.GetLeft(element); var top = InkCanvas.GetTop(element); if (double.IsNaN(left)) left = 0; if (double.IsNaN(top)) top = 0; var width = fe.ActualWidth > 0 ? fe.ActualWidth : fe.Width; var height = fe.ActualHeight > 0 ? fe.ActualHeight : fe.Height; // 检查是否有RenderTransform if (fe.RenderTransform != null && fe.RenderTransform != Transform.Identity) { try { // 如果有变换,使用变换后的边界 var transform = element.TransformToAncestor(inkCanvas); var elementBounds = new Rect(0, 0, width, height); var transformedBounds = transform.TransformBounds(elementBounds); return transformedBounds; } catch { // 变换失败时回退到简单计算 return new Rect(left, top, width, height); } } else { // 没有变换时直接使用位置和大小 return new Rect(left, top, width, height); } } return new Rect(0, 0, 0, 0); } private void UpdateResizeHandlesPosition(Rect bounds) { if (resizeHandles.Count != 8) return; var handleSize = 12.0; var halfHandle = handleSize / 2; // 计算手柄位置 var positions = new[] { new Point(bounds.Left - halfHandle, bounds.Top - halfHandle), // TopLeft new Point(bounds.Left + bounds.Width / 2 - halfHandle, bounds.Top - halfHandle), // TopCenter new Point(bounds.Right - halfHandle, bounds.Top - halfHandle), // TopRight new Point(bounds.Left - halfHandle, bounds.Top + bounds.Height / 2 - halfHandle), // MiddleLeft new Point(bounds.Right - halfHandle, bounds.Top + bounds.Height / 2 - halfHandle), // MiddleRight new Point(bounds.Left - halfHandle, bounds.Bottom - halfHandle), // BottomLeft new Point(bounds.Left + bounds.Width / 2 - halfHandle, bounds.Bottom - halfHandle), // BottomCenter new Point(bounds.Right - halfHandle, bounds.Bottom - halfHandle) // BottomRight }; for (int i = 0; i < resizeHandles.Count && i < positions.Length; i++) { System.Windows.Controls.Canvas.SetLeft(resizeHandles[i], positions[i].X); System.Windows.Controls.Canvas.SetTop(resizeHandles[i], positions[i].Y); } } private void ResizeHandle_MouseDown(object sender, MouseButtonEventArgs e) { if (selectedUIElement == null) return; var handle = sender as Rectangle; if (handle?.Tag is ResizeDirection direction) { isResizing = true; currentResizeDirection = direction; resizeStartPoint = e.GetPosition(inkCanvas); originalElementBounds = GetUIElementBounds(selectedUIElement); handle.CaptureMouse(); e.Handled = true; } } private void ResizeHandle_MouseMove(object sender, MouseEventArgs e) { if (!isResizing || selectedUIElement == null) return; var currentPoint = e.GetPosition(inkCanvas); var deltaX = currentPoint.X - resizeStartPoint.X; var deltaY = currentPoint.Y - resizeStartPoint.Y; ResizeUIElement(deltaX, deltaY); e.Handled = true; } private void ResizeHandle_MouseUp(object sender, MouseButtonEventArgs e) { if (isResizing) { isResizing = false; currentResizeDirection = ResizeDirection.None; var handle = sender as Rectangle; handle?.ReleaseMouseCapture(); e.Handled = true; } } private void ResizeUIElement(double deltaX, double deltaY) { if (selectedUIElement == null) return; var newBounds = originalElementBounds; const double minSize = 20.0; switch (currentResizeDirection) { case ResizeDirection.TopLeft: var newWidth = originalElementBounds.Width - deltaX; var newHeight = originalElementBounds.Height - deltaY; if (newWidth >= minSize && newHeight >= minSize) { newBounds.X = originalElementBounds.X + deltaX; newBounds.Y = originalElementBounds.Y + deltaY; newBounds.Width = newWidth; newBounds.Height = newHeight; } break; case ResizeDirection.TopCenter: var newHeightTC = originalElementBounds.Height - deltaY; if (newHeightTC >= minSize) { newBounds.Y = originalElementBounds.Y + deltaY; newBounds.Height = newHeightTC; } break; case ResizeDirection.TopRight: var newWidthTR = originalElementBounds.Width + deltaX; var newHeightTR = originalElementBounds.Height - deltaY; if (newWidthTR >= minSize && newHeightTR >= minSize) { newBounds.Y = originalElementBounds.Y + deltaY; newBounds.Width = newWidthTR; newBounds.Height = newHeightTR; } break; case ResizeDirection.MiddleLeft: var newWidthML = originalElementBounds.Width - deltaX; if (newWidthML >= minSize) { newBounds.X = originalElementBounds.X + deltaX; newBounds.Width = newWidthML; } break; case ResizeDirection.MiddleRight: var newWidthMR = originalElementBounds.Width + deltaX; if (newWidthMR >= minSize) { newBounds.Width = newWidthMR; } break; case ResizeDirection.BottomLeft: var newWidthBL = originalElementBounds.Width - deltaX; var newHeightBL = originalElementBounds.Height + deltaY; if (newWidthBL >= minSize && newHeightBL >= minSize) { newBounds.X = originalElementBounds.X + deltaX; newBounds.Width = newWidthBL; newBounds.Height = newHeightBL; } break; case ResizeDirection.BottomCenter: var newHeightBC = originalElementBounds.Height + deltaY; if (newHeightBC >= minSize) { newBounds.Height = newHeightBC; } break; case ResizeDirection.BottomRight: var newWidthBR = originalElementBounds.Width + deltaX; var newHeightBR = originalElementBounds.Height + deltaY; if (newWidthBR >= minSize && newHeightBR >= minSize) { newBounds.Width = newWidthBR; newBounds.Height = newHeightBR; } break; } // 应用新的尺寸和位置 ApplyUIElementBounds(selectedUIElement, newBounds); // 更新手柄位置 UpdateResizeHandlesPosition(newBounds); // 如果是图片,也更新工具栏位置 if (selectedUIElement is Image) { UpdateImageToolbarPosition(newBounds); } } private void ApplyUIElementBounds(UIElement element, Rect bounds) { if (element is FrameworkElement fe) { // 清除RenderTransform,避免与直接设置Width/Height冲突 fe.RenderTransform = Transform.Identity; // 直接设置位置和大小 InkCanvas.SetLeft(element, bounds.X); InkCanvas.SetTop(element, bounds.Y); fe.Width = bounds.Width; fe.Height = bounds.Height; } } private void UIElement_MouseDown(object sender, MouseButtonEventArgs e) { if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) { var element = sender as UIElement; if (element != null) { // 切换到选择模式并选择这个元素 inkCanvas.Select(new[] { element }); SelectUIElement(element); e.Handled = true; } } } private void StartMonitoringElementChanges(UIElement element) { // 停止之前的监听 StopMonitoringElementChanges(); if (element == null) return; // 记录初始边界 lastElementBounds = GetUIElementBounds(element); // 创建定时器,定期检查元素边界变化 elementUpdateTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) // 约60FPS的更新频率 }; elementUpdateTimer.Tick += (sender, e) => { if (selectedUIElement == null) { StopMonitoringElementChanges(); return; } var currentBounds = GetUIElementBounds(selectedUIElement); // 检查边界是否发生变化 if (!AreRectsEqual(lastElementBounds, currentBounds)) { lastElementBounds = currentBounds; // 更新手柄位置 UpdateResizeHandlesPosition(currentBounds); // 如果是图片,也更新工具栏位置 if (selectedUIElement is Image) { UpdateImageToolbarPosition(currentBounds); } } }; elementUpdateTimer.Start(); } private void StopMonitoringElementChanges() { if (elementUpdateTimer != null) { elementUpdateTimer.Stop(); elementUpdateTimer = null; } } private bool AreRectsEqual(Rect rect1, Rect rect2) { const double tolerance = 0.1; // 允许的误差范围 return Math.Abs(rect1.X - rect2.X) < tolerance && Math.Abs(rect1.Y - rect2.Y) < tolerance && Math.Abs(rect1.Width - rect2.Width) < tolerance && Math.Abs(rect1.Height - rect2.Height) < tolerance; } #endregion } }