using NPOI.SS.Formula.Functions; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media.Imaging; using System.Windows.Media; using System.Windows; using System.Xml.Serialization; using XHandler.Class; using System.ComponentModel; using XHandler.View.Consumables; using XImagingXhandler.XDAL; using XHandler.View.ManualPick; using XCommon.Tip; using XCommon.Image; namespace XHandler.Controls.DrawCanvas { [ContentProperty] public sealed class DrawingCanvas : Canvas { public int ID = 1; /// /// 确定 /// public static readonly RoutedEvent AddPointRoutedEvent = EventManager.RegisterRoutedEvent("AddPointEvent", RoutingStrategy.Bubble, typeof(CustomEvent.CustomRoutedEventHandler), typeof(DrawingCanvas)); [Description("AddPointEvent")] public event CustomEvent.CustomRoutedEventHandler AddPointEvent { add { this.AddHandler(AddPointRoutedEvent, value); } remove { this.RemoveHandler(AddPointRoutedEvent, value); } } private void RaiseAddPointEvent(PointData data) { CustomRoutedEventArgs arg = new CustomRoutedEventArgs(AddPointRoutedEvent, data); this.RaiseEvent(arg); } public DrawingCanvas() { this.ClipToBounds = true; this.Focusable = true; this.OriginalCursor = Cursors.Arrow; this.squareCursor = false; this.SelectBackgroundPen = new Pen(Brushes.White, 1); this.SelectPen = new Pen(Brushes.Black, 1) { DashStyle = new DashStyle(new Double[] { 4 }, 0) }; this.Loaded += delegate { this.Focus(); this.drawViewer = Utilities.FindVisualParent(this); }; } #region 可视化 private List visuals = new List(); protected override Int32 VisualChildrenCount => visuals.Count + Children.Count; protected override Visual GetVisualChild(Int32 index) { if (index < visuals.Count) return visuals[index]; else return Children[index - visuals.Count]; } public void AddVisual(DrawGeometryBase visual) { visuals.Add(visual); base.AddVisualChild(visual); base.AddLogicalChild(visual); } public void Insert(Int32 index, Visual visual) { visuals.Insert(index, visual); base.AddVisualChild(visual); base.AddLogicalChild(visual); } public void MovoToHead(Visual visual) { var index = visuals.IndexOf(visual); if (index <= 0) return; visuals.RemoveAt(index); visuals.Insert(0, visual); } public void DeleteVisual(Visual visual) { visuals.Remove(visual); base.RemoveVisualChild(visual); base.RemoveLogicalChild(visual); } public Visual GetVisual(int id) { return visuals.FirstOrDefault(it => ((DrawGeometryBase)it).ID == id); } public DrawingVisual GetVisual(Point point) { var hitResult = VisualTreeHelper.HitTest(this, point); if (hitResult != null) { return hitResult.VisualHit as DrawingVisual; } else { return null; } } #endregion #region 依赖属性 public static readonly DependencyProperty BrushProperty = DependencyProperty.Register("Brush", typeof(SolidColorBrush), typeof(DrawingCanvas), new PropertyMetadata(Brushes.Black, OnBrushPropertyChanged)); private static void OnBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((DrawingCanvas)d).UpdateCursor(); } /// /// 画刷颜色 /// public SolidColorBrush Brush { get => (SolidColorBrush)this.GetValue(BrushProperty); set => this.SetValue(BrushProperty, value); } public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register("StrokeThickness", typeof(UInt32), typeof(DrawingCanvas), new PropertyMetadata(1u, OnStrokeThicknessPropertyChanged)); private static void OnStrokeThicknessPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((DrawingCanvas)d).UpdateCursor(); } /// /// 画刷宽度 /// public UInt32 StrokeThickness { get => (UInt32)this.GetValue(StrokeThicknessProperty); set => this.SetValue(StrokeThicknessProperty, value); } public static readonly DependencyProperty DrawingToolTypeProperty = DependencyProperty.Register("DrawingToolType", typeof(DrawToolType), typeof(DrawingCanvas), new PropertyMetadata(DrawToolType.Pointer, OnDrawingToolTypePropertyChanged)); private static void OnDrawingToolTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var drawing = (DrawingCanvas)d; if (!drawing.IsKeyboardFocused) drawing.Focus(); drawing.UpdateCursor(); } /// /// 当前的画图工具 /// public DrawToolType DrawingToolType { get => (DrawToolType)this.GetValue(DrawingToolTypeProperty); set => this.SetValue(DrawingToolTypeProperty, value); } public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeof(DrawingCanvas), new FrameworkPropertyMetadata(12d, FrameworkPropertyMetadataOptions.Inherits)); /// /// 文本大小 /// public Double FontSize { get => (Double)this.GetValue(FontSizeProperty); set => this.SetValue(FontSizeProperty, value); } public static readonly DependencyProperty ZoomProperty = DrawingCanvasViewer.ZoomProperty.AddOwner(typeof(DrawingCanvas), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnZoomPropertyChanged))); private static void OnZoomPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (!Double.IsNaN((Double)e.NewValue)) ((DrawingCanvas)d).UpdateCursor(); } /// /// X轴缩放 /// public Double Zoom { get => (Double)this.GetValue(ZoomProperty); set => this.SetValue(ZoomProperty, value); } #endregion #region 鼠标键盘事件 protected override void OnMouseEnter(MouseEventArgs e) { IDrawTool tool; var point = e.GetPosition(this); for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == 0 && tool.CanTouchEnter && (e.Handled = tool.OnTouchEnter(point))) return; } } protected override void OnMouseLeave(MouseEventArgs e) { lastPoint = null; IDrawTool tool; var point = e.GetPosition(this); for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == 0 && tool.CanTouchLeave && (e.Handled = tool.OnTouchLeave(point))) return; } } #region 鼠标左击 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (!this.IsKeyboardFocused) this.Focus(); if (this.canDragStart) { lastPoint = e.GetPosition(this.drawViewer); e.Handled = true; return; } IDrawTool tool; Point point = e.GetPosition(this); //for (var i = workingDrawTools.Count - 1; i >= 0; i--) //{ // tool = workingDrawTools[i]; // if (tool.CanTouchDown && (e.Handled = tool.OnTouchDown(0, point))) // return; //} //tool = CreateDrawingTool(); //if (tool.CanTouchDown && (e.Handled = tool.OnTouchDown(0, point))) // return; MouseLeftButtonDown(point); if (this.canDragStart) { lastPoint = e.GetPosition(this); e.Handled = true; return; } } public void MouseLeftButtonDown(Point point) { IDrawTool tool = null; //for (var i = workingDrawTools.Count - 1; i >= 0; i--) //{ // tool = workingDrawTools[i]; // if (tool.CanTouchDown && (e.Handled = tool.OnTouchDown(0, point))) // return; //} tool = CreateDrawingTool(); //if (tool.CanTouchDown && (e.Handled = tool.OnTouchDown(0, point))) // return; tool.OnTouchDown(0, point); } #endregion #region 鼠标右击 protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { //lastPoint = null; //IDrawTool tool; Point point = e.GetPosition(this); //for (var i = workingDrawTools.Count - 1; i >= 0; i--) //{ // tool = workingDrawTools[i]; // if (tool.TouchId == 0 && tool.CanTouchUp && (e.Handled = tool.OnTouchLeave(point))) // { // PointData pt = new PointData(); // EllipseDrawTool ellipseDrawTool = (EllipseDrawTool)tool; // //if (ellipseDrawTool.radiusX == 0 || ellipseDrawTool.radiusY == 0) // // return; // pt.id = this.ID - 1; // pt.point = new Point(ellipseDrawTool.center.X, ellipseDrawTool.center.Y); // pt.diameter = Math.Min(ellipseDrawTool.radiusX * 2, ellipseDrawTool.radiusY * 2); // RaiseAddPointEvent(pt); // return; // } //} MouseLeftButtonUp(point); } public void MouseLeftButtonUp(Point point, string coordinateMachine = "") { IDrawTool tool = null; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; //if (tool.TouchId == 0 && tool.CanTouchUp && (e.Handled = tool.OnTouchLeave(point))) { tool.OnTouchLeave(point); PointData pt = new PointData(); EllipseDrawTool ellipseDrawTool = (EllipseDrawTool)tool; pt.id = this.ID - 1; pt.point = new Point(ellipseDrawTool.center.X, ellipseDrawTool.center.Y); pt.diameter = Math.Min(ellipseDrawTool.radiusX * 2, ellipseDrawTool.radiusY * 2); pt.coordinate_machine = coordinateMachine; RaiseAddPointEvent(pt); return; } } lastPoint = null; } #endregion #region 在特定的点位画上文字,表示被删除 public void DrawTextShowDeletedState(Point point) { IDrawTool tool= CreateDrawingTool(); this.Brush = Brushes.Red; tool.OnTouchDown(0, point); SolidColorBrush mybtn1_Brush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 255, 165, 0)); this.Brush = mybtn1_Brush; } #endregion protected override void OnMouseMove(MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { if (this.canDragMove) { var drawPoint = e.GetPosition(this.drawViewer); var dx = lastPoint.Value.X - drawPoint.X; var dy = lastPoint.Value.Y - drawPoint.Y; this.drawViewer.ScrollBy(dx, dy); lastPoint = drawPoint; e.Handled = true; return; } var point = e.GetPosition(this); if (point.X < 0 || point.Y < 0 || point.X > this.ActualWidth || point.Y > this.ActualHeight) return; IDrawTool tool; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == 0 && tool.CanTouchMove && (e.Handled = tool.OnTouchMove(e.GetPosition(this)))) return; } } } protected override void OnKeyDown(KeyEventArgs e) { IDrawTool tool; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.CanKeyDown && (e.Handled = tool.OnKeyDown(e.Key))) return; } if (e.Key == Key.Space) { this.OriginalCursor = DrawCursors.Hand; this.squareCursor = false; } } protected override void OnKeyUp(KeyEventArgs e) { IDrawTool tool; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.CanKeyUp && (e.Handled = tool.OnKeyUp(e.Key))) return; } if (e.Key == Key.Space) this.UpdateCursor(); } #endregion #region 触摸事件 protected override void OnTouchEnter(TouchEventArgs e) { IDrawTool tool; var point = e.GetTouchPoint(this).Position; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == e.TouchDevice.Id && tool.CanTouchEnter && (e.Handled = tool.OnTouchEnter(point))) return; } } protected override void OnTouchLeave(TouchEventArgs e) { lastPoint = null; IDrawTool tool; var point = e.GetTouchPoint(this).Position; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == e.TouchDevice.Id && tool.CanTouchLeave && (e.Handled = tool.OnTouchLeave(point))) return; } } protected override void OnTouchDown(TouchEventArgs e) { if (!this.IsKeyboardFocused) this.Focus(); if (this.canDragStart) { lastPoint = e.GetTouchPoint(this.drawViewer).Position; e.Handled = true; return; } IDrawTool tool; var point = e.GetTouchPoint(this).Position; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.CanTouchDown && (e.Handled = tool.OnTouchDown(0, point))) return; } tool = CreateDrawingTool(); if (tool.CanTouchDown && (e.Handled = tool.OnTouchDown(0, point))) return; if (this.canDragStart) { lastPoint = point; e.Handled = true; return; } } protected override void OnTouchMove(TouchEventArgs e) { if (this.canDragMove) { var drawPoint = e.GetTouchPoint(this.drawViewer).Position; var dx = lastPoint.Value.X - drawPoint.X; var dy = lastPoint.Value.Y - drawPoint.Y; this.drawViewer.ScrollBy(dx, dy); lastPoint = drawPoint; e.Handled = true; return; } var point = e.GetTouchPoint(this).Position; if (point.X < 0 || point.Y < 0 || point.X > this.ActualWidth || point.Y > this.ActualHeight) return; IDrawTool tool; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == 0 && tool.CanTouchMove && (e.Handled = tool.OnTouchMove(point))) return; } } protected override void OnTouchUp(TouchEventArgs e) { lastPoint = null; IDrawTool tool; var point = e.GetTouchPoint(this).Position; for (var i = workingDrawTools.Count - 1; i >= 0; i--) { tool = workingDrawTools[i]; if (tool.TouchId == 0 && tool.CanTouchUp && (e.Handled = tool.OnTouchUp(point))) return; } } #endregion #region 公开方法 public void Clear() { foreach (var visual in visuals) { base.RemoveLogicalChild(visual); base.RemoveVisualChild(visual); } this.visuals.Clear(); this.Children.Clear(); this.workingDrawTools.Clear(); } public IEnumerable GetDrawGeometries() { return visuals.OfType().Where(q => q.IsFinish); } public void AddWorkingDrawTool(IDrawTool drawTool) { this.workingDrawTools.Add(drawTool); } public void DeleteWorkingDrawTool(IDrawTool drawTool) { this.workingDrawTools.Remove(drawTool); } /// /// 转为图片 /// /// /// /// /// /// public BitmapFrame ToBitmapFrame(Int32 pixelWidth, Int32 pixelHeight, Dpi dpi, ImageSource image = null) { var visual = GetDrawingVisual(pixelWidth, pixelHeight, dpi, image); if (visual == null) return null; var renderBitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, dpi.DpiX, dpi.DpiY, PixelFormats.Pbgra32); renderBitmap.Render(visual); return BitmapFrame.Create(renderBitmap); } /// /// 打印 /// /// /// /// /// public void Print(Int32 pixelWidth, Int32 pixelHeight, Dpi dpi, ImageSource image = null) { //var visual = GetDrawingVisual(pixelWidth, pixelHeight, dpi, image); //if (visual == null) // return; //var printDlg = new PrintDialog(); //if ((Boolean)printDlg.ShowDialog()) //{ // var l = Math.Max(pixelWidth, pixelHeight); // var s = Math.Min(pixelWidth, pixelHeight); // var sizeL = Math.Max(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight); // var sizeS = Math.Min(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight); // var zoom = Math.Min(sizeL / l, sizeS / s); // visual.Transform = new ScaleTransform(zoom, zoom); // if (pixelWidth != pixelHeight && (pixelWidth > pixelHeight ^ printDlg.PrintableAreaWidth > printDlg.PrintableAreaHeight)) // printDlg.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape; // printDlg.PrintVisual(visual, nameof(DrawingCanvas)); //} } public void Save(String filepath) { var serializer = new DrawGeometrySerializer { Geometries = this.GetDrawGeometries().Select(q => q.ToSerializer()).ToArray() }; var xml = new XmlSerializer(typeof(DrawGeometrySerializer)); using (var fs = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.None)) { xml.Serialize(fs, serializer); } } public void Load(String filepath) { var xml = new XmlSerializer(typeof(DrawGeometrySerializer)); using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var serializer = (DrawGeometrySerializer)xml.Deserialize(fs); this.Clear(); foreach (var draw in serializer.Geometries) { this.AddVisual(draw.Deserialize(this)); } } } #endregion #region 私有方法 /// /// 创建画图工具 /// /// /// private IDrawTool CreateDrawingTool() { switch (DrawingToolType) { //case DrawToolType.Pointer: // return new PointerDrawTool(this); //case DrawToolType.Pen: // return new PenDrawTool(this); //case DrawToolType.Eraser: // return new EraserDrawTool(this); //case DrawToolType.Line: // return new LineDrawTool(this); //case DrawToolType.Arrow: // return new ArrowDrawTool(this); //case DrawToolType.Ranging: // return new RangingDrawTool(this); //case DrawToolType.Rectangle: // return new RectangleDrawTool(this); case DrawToolType.Ellipse: return new EllipseDrawTool(this); //case DrawToolType.Angle: // return new AngleDrawTool(this); //case DrawToolType.Polyline: // return new PolylineDrawTool(this); //case DrawToolType.Curve: // return new CurveDrawTool(this); //case DrawToolType.Polygon: // return new PolygonDrawTool(this); //case DrawToolType.ClosedCurve: // return new ClosedCurveDrawTool(this); //case DrawToolType.Area: // return new AreaDrawTool(this); //case DrawToolType.Text: // return new TextDrawTool(this); default: throw new ArgumentOutOfRangeException("不支持画图工具" + DrawingToolType); } } private void UpdateCursor() { switch (this.DrawingToolType) { case DrawToolType.Pointer: this.OriginalCursor = Cursors.Arrow; this.squareCursor = false; break; case DrawToolType.Text: this.OriginalCursor = Cursors.IBeam; this.squareCursor = false; break; case DrawToolType.Ellipse: this.OriginalCursor = Cursors.Cross; this.squareCursor = false; break; default: if ((squareCursor && cursorLength == StrokeThickness && cursorZoom == Zoom) || Double.IsNaN(StrokeThickness) || Double.IsNaN(Zoom)) break; var w = (UInt32)Math.Max(1, StrokeThickness * Zoom); var h = (UInt32)Math.Max(1, StrokeThickness * Zoom); var border = (UInt32)Math.Max(1, 2 * Math.Min(1, Zoom)); this.OriginalCursor = DrawCursors.CreateBmpCursor(w, h, border, Brush); this.squareCursor = true; this.cursorLength = StrokeThickness; this.cursorZoom = Zoom; break; } } private DrawingVisual GetDrawingVisual(Int32 pixelWidth, Int32 pixelHeight, Dpi dpi, ImageSource image = null) { var root = new DrawingVisual(); var dc = root.RenderOpen(); var drawGeometries = this.GetDrawGeometries(); if (image != null) dc.DrawImage(image, new Rect(new Size(pixelWidth * dpi.Px2WpfX, pixelHeight * dpi.Px2WpfY))); foreach (var draw in drawGeometries) { dc.DrawDrawing(draw.Drawing); } dc.Close(); return root; } #endregion #region 属性 /// /// 画笔 /// public Pen Pen => new Pen(this.Brush, this.StrokeThickness); /// /// 选择画笔 /// public Pen SelectPen { get; } /// /// 选择底色画笔 /// public Pen SelectBackgroundPen { get; } #endregion #region 字段 /// /// 正在进行的画图工具 /// private List workingDrawTools = new List(); private Cursor originalCursor; /// /// 原始指针 /// public Cursor OriginalCursor { get => originalCursor; set { if (originalCursor == value) return; originalCursor = value; if (!handleCursor) this.Cursor = value; } } /// /// 鼠标指针是否正在被控制 /// public Boolean handleCursor; public Boolean squareCursor; public UInt32 cursorLength; public Double cursorZoom; private Point? lastPoint; private DrawingCanvasViewer drawViewer; private Boolean canDragStart => this.Cursor == DrawCursors.Hand && drawViewer != null; private Boolean canDragMove => canDragStart && lastPoint.HasValue; #endregion } }