2025-08-03 16:46:33 +08:00
|
|
|
using iNKORE.UI.WPF.Modern.Controls;
|
2025-05-25 09:29:48 +08:00
|
|
|
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;
|
2025-07-28 11:15:35 +08:00
|
|
|
using System.Windows.Shapes;
|
2025-07-30 14:18:45 +08:00
|
|
|
using System.Windows.Threading;
|
2025-05-25 09:29:48 +08:00
|
|
|
using Point = System.Windows.Point;
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
namespace Ink_Canvas
|
|
|
|
|
{
|
|
|
|
|
public partial class MainWindow : Window
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
#region Floating Control
|
|
|
|
|
|
|
|
|
|
private object lastBorderMouseDownObject;
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void Border_MouseDown(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-06-17 18:21:14 +08:00
|
|
|
// 如果发送者是 RandomDrawPanel 或 SingleDrawPanel,且它们被隐藏,则不处理事件
|
2025-08-03 16:46:33 +08:00
|
|
|
if (sender is SimpleStackPanel panel)
|
|
|
|
|
{
|
|
|
|
|
if ((panel == RandomDrawPanel || panel == SingleDrawPanel) &&
|
|
|
|
|
panel.Visibility != Visibility.Visible)
|
|
|
|
|
{
|
2025-06-17 18:21:14 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-25 09:29:48 +08:00
|
|
|
lastBorderMouseDownObject = sender;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 14:40:44 +08:00
|
|
|
private bool isStrokeSelectionCloneOn;
|
2025-05-25 09:29:48 +08:00
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void BorderStrokeSelectionClone_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (lastBorderMouseDownObject != sender) return;
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
if (isStrokeSelectionCloneOn)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
BorderStrokeSelectionClone.Background = Brushes.Transparent;
|
|
|
|
|
|
|
|
|
|
isStrokeSelectionCloneOn = false;
|
|
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
BorderStrokeSelectionClone.Background = new SolidColorBrush(StringToColor("#FF1ED760"));
|
|
|
|
|
|
|
|
|
|
isStrokeSelectionCloneOn = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void BorderStrokeSelectionCloneToNewBoard_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (lastBorderMouseDownObject != sender) return;
|
|
|
|
|
|
|
|
|
|
var strokes = inkCanvas.GetSelectedStrokes();
|
|
|
|
|
inkCanvas.Select(new StrokeCollection());
|
|
|
|
|
strokes = strokes.Clone();
|
|
|
|
|
BtnWhiteBoardAdd_Click(null, null);
|
|
|
|
|
inkCanvas.Strokes.Add(strokes);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void BorderStrokeSelectionDelete_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (lastBorderMouseDownObject != sender) return;
|
|
|
|
|
SymbolIconDelete_MouseUp(sender, e);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridPenWidthDecrease_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (lastBorderMouseDownObject != sender) return;
|
|
|
|
|
ChangeStrokeThickness(0.8);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridPenWidthIncrease_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (lastBorderMouseDownObject != sender) return;
|
|
|
|
|
ChangeStrokeThickness(1.25);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void ChangeStrokeThickness(double multipler)
|
|
|
|
|
{
|
|
|
|
|
foreach (var stroke in inkCanvas.GetSelectedStrokes())
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
foreach (var item in DrawingAttributesHistoryFlag)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridPenWidthRestore_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (lastBorderMouseDownObject != sender) return;
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
foreach (var stroke in inkCanvas.GetSelectedStrokes())
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
stroke.DrawingAttributes.Width = inkCanvas.DefaultDrawingAttributes.Width;
|
|
|
|
|
stroke.DrawingAttributes.Height = inkCanvas.DefaultDrawingAttributes.Height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void ImageFlipHorizontal_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
foreach (var item in DrawingAttributesHistoryFlag)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//updateBorderStrokeSelectionControlLocation();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void ImageFlipVertical_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
foreach (var item in DrawingAttributesHistoryFlag)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
// ... existing code ...
|
|
|
|
|
private void ImageRotate45_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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
|
2025-07-31 13:23:33 +08:00
|
|
|
m.RotateAt(45, center.X, center.Y); // 顺时针旋转45度
|
2025-05-25 09:29:48 +08:00
|
|
|
|
|
|
|
|
var targetStrokes = inkCanvas.GetSelectedStrokes();
|
|
|
|
|
foreach (var stroke in targetStrokes) stroke.Transform(m, false);
|
|
|
|
|
|
|
|
|
|
if (DrawingAttributesHistory.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory);
|
|
|
|
|
DrawingAttributesHistory = new Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
foreach (var item in DrawingAttributesHistoryFlag)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void ImageRotate90_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
foreach (var item in DrawingAttributesHistoryFlag)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2025-07-28 14:40:44 +08:00
|
|
|
private bool isGridInkCanvasSelectionCoverMouseDown;
|
2025-05-25 09:29:48 +08:00
|
|
|
private StrokeCollection StrokesSelectionClone = new StrokeCollection();
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_MouseDown(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
isGridInkCanvasSelectionCoverMouseDown = true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_MouseUp(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (!isGridInkCanvasSelectionCoverMouseDown) return;
|
|
|
|
|
isGridInkCanvasSelectionCoverMouseDown = false;
|
|
|
|
|
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void BtnSelect_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-07-18 17:59:17 +08:00
|
|
|
ExitMultiTouchModeIfNeeded();
|
2025-05-25 09:29:48 +08:00
|
|
|
forceEraser = true;
|
|
|
|
|
drawingShapeMode = 0;
|
|
|
|
|
inkCanvas.IsManipulationEnabled = false;
|
2025-08-03 16:46:33 +08:00
|
|
|
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
|
|
|
|
|
{
|
|
|
|
|
if (inkCanvas.GetSelectedStrokes().Count == inkCanvas.Strokes.Count)
|
|
|
|
|
{
|
2025-08-30 11:20:53 +08:00
|
|
|
// 使用集中化的工具模式切换方法
|
|
|
|
|
SetCurrentToolMode(InkCanvasEditingMode.Ink, () => {
|
|
|
|
|
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
|
|
|
|
});
|
2025-05-25 09:29:48 +08:00
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else
|
|
|
|
|
{
|
2025-08-30 11:20:53 +08:00
|
|
|
// 使用集中化的工具模式切换方法
|
|
|
|
|
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
2025-05-25 09:29:48 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double BorderStrokeSelectionControlWidth = 490.0;
|
|
|
|
|
private double BorderStrokeSelectionControlHeight = 80.0;
|
2025-07-28 14:40:44 +08:00
|
|
|
private bool isProgramChangeStrokeSelection;
|
2025-05-25 09:29:48 +08:00
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void inkCanvas_SelectionChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
if (isProgramChangeStrokeSelection) return;
|
2025-08-03 16:46:33 +08:00
|
|
|
if (inkCanvas.GetSelectedStrokes().Count == 0)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
2025-07-28 11:15:35 +08:00
|
|
|
// 当没有选中笔画时,检查是否有选中的UIElement
|
|
|
|
|
CheckUIElementSelection();
|
2025-05-25 09:29:48 +08:00
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
GridInkCanvasSelectionCover.Visibility = Visibility.Visible;
|
|
|
|
|
BorderStrokeSelectionClone.Background = Brushes.Transparent;
|
|
|
|
|
isStrokeSelectionCloneOn = false;
|
|
|
|
|
updateBorderStrokeSelectionControlLocation();
|
2025-07-28 11:15:35 +08:00
|
|
|
// 当选中笔画时,取消UIElement选择
|
|
|
|
|
DeselectUIElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckUIElementSelection()
|
|
|
|
|
{
|
|
|
|
|
// 检查InkCanvas中的UIElement是否被选中
|
|
|
|
|
var selectedElements = inkCanvas.GetSelectedElements();
|
|
|
|
|
if (selectedElements.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var element = selectedElements[0];
|
|
|
|
|
SelectUIElement(element);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DeselectUIElement();
|
2025-05-25 09:29:48 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void updateBorderStrokeSelectionControlLocation()
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
e.Mode = ManipulationModes.All;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
foreach (var item in DrawingAttributesHistoryFlag)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (dec.Count >= 1)
|
|
|
|
|
{
|
2025-07-23 22:57:16 +08:00
|
|
|
bool disableScale = dec.Count >= 3;
|
2025-05-25 09:29:48 +08:00
|
|
|
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); // 移动
|
2025-06-19 22:49:11 +08:00
|
|
|
if (!disableScale)
|
|
|
|
|
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
|
2025-05-25 09:29:48 +08:00
|
|
|
|
|
|
|
|
var strokes = inkCanvas.GetSelectedStrokes();
|
|
|
|
|
if (StrokesSelectionClone.Count != 0)
|
|
|
|
|
strokes = StrokesSelectionClone;
|
|
|
|
|
else if (Settings.Gesture.IsEnableTwoFingerRotationOnSelection)
|
|
|
|
|
m.RotateAt(rotate, center.X, center.Y); // 旋转
|
2025-08-03 16:46:33 +08:00
|
|
|
foreach (var stroke in strokes)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
stroke.Transform(m, false);
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
try
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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);
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_PreviewTouchDown(object sender, TouchEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
dec.Add(e.TouchDevice.Id);
|
|
|
|
|
//设备1个的时候,记录中心点
|
2025-08-03 16:46:33 +08:00
|
|
|
if (dec.Count == 1)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
var touchPoint = e.GetTouchPoint(null);
|
|
|
|
|
centerPoint = touchPoint.Position;
|
|
|
|
|
lastTouchPointOnGridInkCanvasCover = touchPoint.Position;
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
if (isStrokeSelectionCloneOn)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
var strokes = inkCanvas.GetSelectedStrokes();
|
|
|
|
|
isProgramChangeStrokeSelection = true;
|
|
|
|
|
inkCanvas.Select(new StrokeCollection());
|
|
|
|
|
StrokesSelectionClone = strokes.Clone();
|
|
|
|
|
inkCanvas.Select(strokes);
|
|
|
|
|
isProgramChangeStrokeSelection = false;
|
|
|
|
|
inkCanvas.Strokes.Add(StrokesSelectionClone);
|
|
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else
|
|
|
|
|
{
|
2025-06-17 13:20:06 +08:00
|
|
|
// 新增:启动套索选择模式
|
2025-08-30 11:20:53 +08:00
|
|
|
// 使用集中化的工具模式切换方法
|
|
|
|
|
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
2025-06-17 13:20:06 +08:00
|
|
|
inkCanvas.Select(new StrokeCollection());
|
|
|
|
|
}
|
2025-05-25 09:29:48 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void GridInkCanvasSelectionCover_PreviewTouchUp(object sender, TouchEventArgs e)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
dec.Remove(e.TouchDevice.Id);
|
|
|
|
|
if (dec.Count >= 1) return;
|
|
|
|
|
isProgramChangeStrokeSelection = false;
|
2025-08-03 16:46:33 +08:00
|
|
|
if (lastTouchPointOnGridInkCanvasCover == e.GetTouchPoint(null).Position)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
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();
|
|
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else if (inkCanvas.GetSelectedStrokes().Count == 0)
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
|
|
|
|
StrokesSelectionClone = new StrokeCollection();
|
|
|
|
|
}
|
2025-08-03 16:46:33 +08:00
|
|
|
else
|
|
|
|
|
{
|
2025-05-25 09:29:48 +08:00
|
|
|
GridInkCanvasSelectionCover.Visibility = Visibility.Visible;
|
|
|
|
|
StrokesSelectionClone = new StrokeCollection();
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-18 16:12:04 +08:00
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void LassoSelect_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-07-18 17:59:17 +08:00
|
|
|
ExitMultiTouchModeIfNeeded();
|
2025-07-18 16:12:04 +08:00
|
|
|
forceEraser = false;
|
|
|
|
|
forcePointEraser = false;
|
|
|
|
|
drawingShapeMode = 0;
|
2025-08-30 11:20:53 +08:00
|
|
|
// 使用集中化的工具模式切换方法
|
|
|
|
|
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
2025-07-18 16:12:04 +08:00
|
|
|
SetCursorBasedOnEditingMode(inkCanvas);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 16:46:33 +08:00
|
|
|
private void BtnLassoSelect_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-07-18 17:59:17 +08:00
|
|
|
ExitMultiTouchModeIfNeeded();
|
2025-07-18 16:12:04 +08:00
|
|
|
forceEraser = false;
|
|
|
|
|
forcePointEraser = false;
|
|
|
|
|
drawingShapeMode = 0;
|
2025-08-30 11:20:53 +08:00
|
|
|
// 使用集中化的工具模式切换方法
|
|
|
|
|
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
2025-07-18 16:12:04 +08:00
|
|
|
inkCanvas.IsManipulationEnabled = true;
|
|
|
|
|
SetCursorBasedOnEditingMode(inkCanvas);
|
|
|
|
|
}
|
2025-07-28 11:15:35 +08:00
|
|
|
|
|
|
|
|
#region UIElement Selection and Resize
|
|
|
|
|
|
2025-07-28 14:40:44 +08:00
|
|
|
private UIElement selectedUIElement;
|
|
|
|
|
private System.Windows.Controls.Canvas resizeHandlesCanvas;
|
2025-07-28 11:15:35 +08:00
|
|
|
private readonly List<Rectangle> resizeHandles = new List<Rectangle>();
|
2025-07-28 14:40:44 +08:00
|
|
|
private bool isResizing;
|
2025-07-28 11:15:35 +08:00
|
|
|
private ResizeDirection currentResizeDirection = ResizeDirection.None;
|
|
|
|
|
private Point resizeStartPoint;
|
|
|
|
|
private Rect originalElementBounds;
|
|
|
|
|
|
2025-07-28 19:02:45 +08:00
|
|
|
// 图片工具栏相关
|
|
|
|
|
private Border borderImageSelectionControl;
|
2025-07-31 13:23:33 +08:00
|
|
|
private double BorderImageSelectionControlWidth = 490.0; // 6个按钮 + 分隔线的实际宽度
|
2025-07-28 19:02:45 +08:00
|
|
|
private double BorderImageSelectionControlHeight = 80.0;
|
|
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
// 元素变化监听相关
|
|
|
|
|
private DispatcherTimer elementUpdateTimer;
|
|
|
|
|
private Rect lastElementBounds;
|
|
|
|
|
|
2025-07-28 11:15:35 +08:00
|
|
|
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); // 确保在最上层
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 19:02:45 +08:00
|
|
|
// 初始化图片工具栏引用
|
|
|
|
|
if (borderImageSelectionControl == null)
|
|
|
|
|
{
|
|
|
|
|
borderImageSelectionControl = FindName("BorderImageSelectionControl") as Border;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 11:15:35 +08:00
|
|
|
// 创建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();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-30 14:12:10 +08:00
|
|
|
// 显示拖拽手柄(所有UI元素都需要)
|
|
|
|
|
ShowResizeHandles();
|
|
|
|
|
|
|
|
|
|
// 根据元素类型显示特定的工具栏
|
2025-07-28 19:02:45 +08:00
|
|
|
if (element is Image)
|
|
|
|
|
{
|
|
|
|
|
ShowImageToolbar();
|
|
|
|
|
}
|
2025-07-30 14:18:45 +08:00
|
|
|
|
|
|
|
|
// 监听元素的布局变化,以便实时更新手柄位置
|
|
|
|
|
StartMonitoringElementChanges(element);
|
2025-07-28 11:15:35 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeselectUIElement()
|
|
|
|
|
{
|
2025-07-30 14:18:45 +08:00
|
|
|
// 停止监听之前选中元素的变化
|
|
|
|
|
StopMonitoringElementChanges();
|
|
|
|
|
|
2025-07-28 11:15:35 +08:00
|
|
|
selectedUIElement = null;
|
|
|
|
|
HideResizeHandles();
|
2025-07-28 19:02:45 +08:00
|
|
|
HideImageToolbar();
|
2025-07-28 11:15:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 19:02:45 +08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 11:15:35 +08:00
|
|
|
private Rect GetUIElementBounds(UIElement element)
|
|
|
|
|
{
|
2025-07-30 14:18:45 +08:00
|
|
|
if (element is FrameworkElement fe)
|
|
|
|
|
{
|
|
|
|
|
var left = InkCanvas.GetLeft(element);
|
|
|
|
|
var top = InkCanvas.GetTop(element);
|
2025-07-28 11:15:35 +08:00
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
if (double.IsNaN(left)) left = 0;
|
|
|
|
|
if (double.IsNaN(top)) top = 0;
|
2025-07-28 11:15:35 +08:00
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
var width = fe.ActualWidth > 0 ? fe.ActualWidth : fe.Width;
|
|
|
|
|
var height = fe.ActualHeight > 0 ? fe.ActualHeight : fe.Height;
|
2025-07-28 11:15:35 +08:00
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
// 检查是否有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);
|
|
|
|
|
}
|
2025-07-28 11:15:35 +08:00
|
|
|
}
|
|
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
return new Rect(0, 0, 0, 0);
|
2025-07-28 11:15:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2025-07-30 14:18:45 +08:00
|
|
|
|
|
|
|
|
// 如果是图片,也更新工具栏位置
|
|
|
|
|
if (selectedUIElement is Image)
|
|
|
|
|
{
|
|
|
|
|
UpdateImageToolbarPosition(newBounds);
|
|
|
|
|
}
|
2025-07-28 11:15:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyUIElementBounds(UIElement element, Rect bounds)
|
|
|
|
|
{
|
|
|
|
|
if (element is FrameworkElement fe)
|
|
|
|
|
{
|
2025-07-30 14:18:45 +08:00
|
|
|
// 清除RenderTransform,避免与直接设置Width/Height冲突
|
|
|
|
|
fe.RenderTransform = Transform.Identity;
|
|
|
|
|
|
|
|
|
|
// 直接设置位置和大小
|
|
|
|
|
InkCanvas.SetLeft(element, bounds.X);
|
|
|
|
|
InkCanvas.SetTop(element, bounds.Y);
|
2025-07-28 11:15:35 +08:00
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
// 切换到选择模式并选择这个元素
|
2025-07-28 14:40:44 +08:00
|
|
|
inkCanvas.Select(new[] { element });
|
2025-07-28 11:15:35 +08:00
|
|
|
SelectUIElement(element);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
private void StartMonitoringElementChanges(UIElement element)
|
|
|
|
|
{
|
|
|
|
|
// 停止之前的监听
|
|
|
|
|
StopMonitoringElementChanges();
|
|
|
|
|
|
|
|
|
|
if (element == null) return;
|
2025-07-28 19:02:45 +08:00
|
|
|
|
2025-07-30 14:18:45 +08:00
|
|
|
// 记录初始边界
|
|
|
|
|
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;
|
|
|
|
|
}
|
2025-07-28 19:02:45 +08:00
|
|
|
|
2025-07-28 11:15:35 +08:00
|
|
|
#endregion
|
2025-05-25 09:29:48 +08:00
|
|
|
}
|
|
|
|
|
}
|