├── CanvasDemo
├── .cr
│ └── personal
│ │ └── FavoritesList
│ │ └── List.xml
├── CanvasDemo
│ ├── Canvas
│ │ ├── IElementData.cs
│ │ ├── IToolTipElement.cs
│ │ ├── Backgrounder.cs
│ │ ├── Element.cs
│ │ ├── FocusElement.cs
│ │ ├── Layer.cs
│ │ ├── PointExtension.cs
│ │ ├── SelectionBox.cs
│ │ ├── ObjectElement.cs
│ │ ├── Canvas.resx
│ │ ├── Viewer.cs
│ │ ├── Canvas.cs
│ │ └── ElementEditor.cs
│ ├── Data
│ │ └── ElementData.cs
│ ├── Program.cs
│ ├── Painter
│ │ ├── CubeLayer.cs
│ │ ├── EllipseLayer.cs
│ │ ├── EllipseElement.cs
│ │ ├── ToolTipComponent.cs
│ │ └── CubeElement.cs
│ ├── CanvasDemo.csproj
│ ├── Properties
│ │ ├── Resources.Designer.cs
│ │ └── Resources.resx
│ ├── Form1.cs
│ ├── ClassDiagram1.cd
│ ├── Form1.resx
│ └── Form1.Designer.cs
└── CanvasDemo.sln
├── README.md
└── .gitignore
/CanvasDemo/.cr/personal/FavoritesList/List.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/IElementData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CanvasDemo.Canvas
8 | {
9 | ///
10 | /// 每个对象的身份
11 | ///
12 | public interface IElementData
13 | {
14 | ///
15 | /// 编号
16 | ///
17 | string ID { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Data/ElementData.cs:
--------------------------------------------------------------------------------
1 | using CanvasDemo.Canvas;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CanvasDemo.Data
9 | {
10 | public class ElementData : IElementData
11 | {
12 | public string ID { get; set; }
13 |
14 | public int Group { get; set; }
15 |
16 | public string Title { get; set; }
17 |
18 | public bool IsError { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 |
3 | 我不知道这个东西应该叫什么名字,是多年前做工厂控制相关时开发的一个组件,他用于配置设备平面图,以及显示设备状态的控制面板。
4 |
5 | 组件采用WinForm的GDI+技术进行绘图,技术上有点陈旧,不过设计还算马虎,后来我有使用相同的设计比较容易的移植到HTML5的Canvas中。
6 |
7 | # 基本功能
8 |
9 | - 支持移动缩放
10 | - 支持多图层
11 | - 正向和反向框选
12 | - 元素调整大小
13 | - 元素状态动态刷新
14 | - 鼠标悬停检测
15 | - 元素快速定位
16 | - 支持数万元素显示
17 | - 等等...
18 |
19 | 
20 |
21 | # 文件夹
22 |
23 | - Canvas
24 |
25 | 画布核心代码,提供缩放,框选等组件的和兴功能
26 |
27 | - Painter
28 |
29 | 业务中使用的组件,图层等代码,可以在这里进行扩展需要显示的元素
30 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 |
7 | namespace CanvasDemo
8 | {
9 | static class Program
10 | {
11 | ///
12 | /// The main entry point for the application.
13 | ///
14 | [STAThread]
15 | static void Main()
16 | {
17 | Application.SetHighDpiMode(HighDpiMode.SystemAware);
18 | Application.EnableVisualStyles();
19 | Application.SetCompatibleTextRenderingDefault(false);
20 | Application.Run(new Form1());
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/IToolTipElement.cs:
--------------------------------------------------------------------------------
1 | using CanvasDemo.Canvas;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Drawing;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace CanvasDemo.Canvas
10 | {
11 | public interface IToolTipElement
12 | {
13 | ///
14 | /// 获得提示的内容
15 | ///
16 | ///
17 | string GetToolTipTitle();
18 | }
19 |
20 | public interface IToolTip
21 | {
22 | void Show(IToolTipElement element);
23 |
24 | void Hide();
25 |
26 | void Drawing(Graphics g);
27 |
28 | void DrawingAfter(Graphics g);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Painter/CubeLayer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Drawing;
7 | using CanvasDemo.Canvas;
8 |
9 | namespace CanvasDemo.Painter
10 | {
11 | public class CubeLayer : Layer
12 | {
13 | public CubeLayer(TimCanvas canvas) : base(canvas, "Cube")
14 | {
15 | IsInteractionLayer = true;
16 | }
17 |
18 | public override void Drawing(Graphics g)
19 | {
20 | foreach (var item in Elements)
21 | {
22 | if (Canvas.Viewer.IsInZone(item) == false) continue;
23 | item.Drawing(g);
24 | }
25 | }
26 |
27 | public override void DrawingAfter(Graphics g)
28 | {
29 |
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Painter/EllipseLayer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Drawing;
7 | using CanvasDemo.Canvas;
8 |
9 | namespace CanvasDemo.Painter
10 | {
11 | public class EllipseLayer : Layer
12 | {
13 | public EllipseLayer(TimCanvas canvas) : base(canvas, "Cube")
14 | {
15 | IsInteractionLayer = true;
16 | }
17 |
18 | public override void Drawing(Graphics g)
19 | {
20 | foreach (var item in Elements)
21 | {
22 | if (Canvas.Viewer.IsInZone(item) == false) continue;
23 | item.Drawing(g);
24 | }
25 | }
26 |
27 | public override void DrawingAfter(Graphics g)
28 | {
29 |
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/CanvasDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net6.0-windows
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | True
18 | True
19 | Resources.resx
20 |
21 |
22 |
23 |
24 |
25 | ResXFileCodeGenerator
26 | Resources.Designer.cs
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Painter/EllipseElement.cs:
--------------------------------------------------------------------------------
1 | using CanvasDemo.Canvas;
2 | using CanvasDemo.Data;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.Drawing.Imaging;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using System.Windows.Forms;
12 |
13 | namespace CanvasDemo.Painter
14 | {
15 | public class EllipseElement : ObjElement
16 | {
17 | public EllipseElement(EllipseLayer layer, ElementData data, int sideLength) : base(layer, data)
18 | {
19 | this.Rect.Width = sideLength;
20 | this.Rect.Height = sideLength;
21 | }
22 |
23 | public static Brush FillBrush = new SolidBrush(Color.Green);
24 |
25 | public override void Drawing(Graphics g)
26 | {
27 | g.FillEllipse(FillBrush, Viewer.LocalToShow(Rect));
28 | }
29 |
30 | public override void DrawingAfter(Graphics g)
31 | {
32 |
33 | }
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/Backgrounder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CanvasDemo.Canvas
9 | {
10 |
11 | ///
12 | /// 控制背景
13 | ///
14 | public class Backgrounder
15 | {
16 | TimCanvas Canvas;
17 |
18 | public Backgrounder(TimCanvas canvas)
19 | {
20 | Canvas = canvas;
21 | }
22 |
23 | Pen ZeroLinePen = new Pen(new SolidBrush(Color.Black),2);
24 |
25 | public void Drawing(Graphics g)
26 | {
27 | var v = Canvas.Viewer.Viewport;
28 |
29 | var vP1 = new Point(0 , v.Y);
30 | var vP2 = new Point(0 , v.Y + v.Height);
31 | g.DrawLine(ZeroLinePen, Canvas.Viewer.LocalToShow(vP1), Canvas.Viewer.LocalToShow(vP2));
32 |
33 | var hP1 = new Point(v.Left, 0);
34 | var hP2 = new Point(v.Left+v.Width, 0);
35 | g.DrawLine(ZeroLinePen, Canvas.Viewer.LocalToShow(hP1), Canvas.Viewer.LocalToShow(hP2));
36 | }
37 |
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31424.327
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanvasDemo", "CanvasDemo\CanvasDemo.csproj", "{11BA9475-E37B-4AE4-AE09-EA99BFD0CCCE}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {11BA9475-E37B-4AE4-AE09-EA99BFD0CCCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {11BA9475-E37B-4AE4-AE09-EA99BFD0CCCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {11BA9475-E37B-4AE4-AE09-EA99BFD0CCCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {11BA9475-E37B-4AE4-AE09-EA99BFD0CCCE}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {5B130506-20FB-4589-8745-FEEBB901924C}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/Element.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CanvasDemo.Canvas
9 | {
10 |
11 | public abstract class Element
12 | {
13 | public string ID { get; set; }
14 | ///
15 | /// 当前对象的区域范围
16 | ///
17 | public Rectangle Rect;//不能用属性,因为属性不能给修改内部值
18 |
19 | ///
20 | /// 正常绘图
21 | ///
22 | ///
23 | public abstract void Drawing(Graphics g);
24 |
25 | ///
26 | /// 第二次绘制,用于显示一些在前端的文字等
27 | ///
28 | public abstract void DrawingAfter(Graphics g);
29 |
30 | ///
31 | /// 画布
32 | ///
33 | protected TimCanvas Canvas;
34 |
35 | ///
36 | /// 视图
37 | ///
38 | protected Viewer Viewer;
39 |
40 | public Element(TimCanvas canvas,string id)
41 | {
42 | Canvas = canvas;
43 | Viewer = canvas.Viewer;
44 | ID = id;
45 | }
46 |
47 |
48 | }
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Painter/ToolTipComponent.cs:
--------------------------------------------------------------------------------
1 | using CanvasDemo.Canvas;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Drawing;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Forms;
9 |
10 | namespace CanvasDemo.Painter
11 | {
12 | public class ToolTipComponent : IToolTip
13 | {
14 | TimCanvas Canvas;
15 |
16 | public ToolTipComponent(TimCanvas canvas)
17 | {
18 | Canvas = canvas;
19 | }
20 |
21 | public void Drawing(Graphics g)
22 | {
23 |
24 | }
25 |
26 | public void DrawingAfter(Graphics g)
27 | {
28 |
29 | }
30 |
31 | public void Hide()
32 | {
33 | if (LastCube != null)
34 | {
35 | LastCube.IsHover = false;
36 | LastCube = null;
37 | Canvas.Refresh();
38 | }
39 | }
40 |
41 | CubeElement LastCube;
42 |
43 | public void Show(IToolTipElement element)
44 | {
45 | if (element is CubeElement cube && LastCube != cube)
46 | {
47 | if (LastCube != null) LastCube.IsHover = false;
48 |
49 |
50 | LastCube = cube;
51 | LastCube.IsHover = true;
52 |
53 | Canvas.Refresh();
54 | }
55 |
56 |
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/FocusElement.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CanvasDemo.Canvas
9 | {
10 | ///
11 | /// 绘制十字焦点
12 | ///
13 | public class FocusElement : Element
14 | {
15 | public bool IsShow { get; set; } = false;
16 | public Point Focus { get; private set; }
17 |
18 | //public Point H1, H2;
19 |
20 | //public Point V1, V2;
21 |
22 | public void SetFocus(Point focus)
23 | {
24 | Focus = focus;
25 | //H1 = new Point(focus.X, 0);
26 | //H2 = new Point(focus.X, this.Canvas.Height);
27 |
28 | //V1 = new Point(0, focus.Y);
29 | //V2 = new Point(this.Canvas.Width, focus.Y);
30 |
31 | }
32 |
33 | public FocusElement(Canvas.TimCanvas canvas) : base(canvas,nameof(FocusElement))
34 | {
35 | }
36 |
37 | public override void Drawing(Graphics g)
38 | {
39 | var focus = this.Canvas.Viewer.LocalToShow(Focus);
40 |
41 | g.DrawLine(Pens.Black, new Point(focus.X, 0), new Point(focus.X, this.Canvas.Height));
42 | g.DrawLine(Pens.Black, new Point(0, focus.Y), new Point(this.Canvas.Width, focus.Y));
43 | }
44 |
45 | public override void DrawingAfter(Graphics g)
46 | {
47 |
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/Layer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Reflection.Emit;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Forms;
9 |
10 | namespace CanvasDemo.Canvas
11 | {
12 | public abstract class Layer
13 | {
14 | public string Name { get; set; }
15 |
16 | public TimCanvas Canvas { get; set; }
17 |
18 | public List Elements { get; set; } = new List();
19 |
20 | ///
21 | /// 是否被激活,只有激活状态的图层上的对象才能被选择
22 | ///
23 | public bool IsActive { get; set; } = false;
24 |
25 | ///
26 | /// 图层是否可见,但图层多的时候便于操作
27 | ///
28 | public bool IsVisible { get; set; } = true;
29 |
30 | ///
31 | /// 是否是互动图层,就是用于交互操作的图层,通常用于放置对象,与他对应的就是辅助图层,用于显示辅助内容,没有交互操作
32 | ///
33 | public bool IsInteractionLayer { get; set; } = false;
34 |
35 | public Layer(TimCanvas canvas, string name)
36 | {
37 | Canvas = canvas;
38 | Name = name;
39 | Canvas.Layers.Add(this);
40 | }
41 |
42 | ///
43 | /// 正常绘图
44 | ///
45 | ///
46 | public abstract void Drawing(Graphics g);
47 |
48 | ///
49 | /// 第二次绘制,用于显示一些在前端的文字等
50 | ///
51 | public abstract void DrawingAfter(Graphics g);
52 |
53 | public virtual bool MouseDown(MouseEventArgs e) => false;
54 | public virtual bool MouseMove(MouseEventArgs e) => false;
55 | public virtual bool MouseUp(MouseEventArgs e) => false;
56 | public virtual bool MouseWheel(MouseEventArgs e) => false;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/PointExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CanvasDemo.Canvas
9 | {
10 | public static class PointExtension
11 | {
12 | public static Point ToPoint(this Point source)
13 | {
14 | return new Point(
15 | Convert.ToInt32(source.X),
16 | Convert.ToInt32(source.Y));
17 | }
18 |
19 | ///
20 | /// 两个坐标围城的尺寸
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | public static Size Subtract(this Point source, Point pt)
27 | {
28 | return new Size(source.X - pt.X, source.Y - pt.Y);
29 | }
30 |
31 |
32 | ///
33 | /// 计算两点之间的距离
34 | ///
35 | ///
36 | ///
37 | ///
38 | public static double Distance(this Point source, Point pt)
39 | {
40 | return Math.Sqrt((source.X - pt.X) * (source.X - pt.X) + (source.Y - pt.Y) * (source.Y - pt.Y));
41 | }
42 |
43 |
44 | ///
45 | /// 判断当前点是否在一组点组成的多边形中
46 | ///
47 | public static bool IsInZone(this Point point, Point[] points)
48 | {
49 | System.Drawing.Drawing2D.GraphicsPath myGraphicsPath = new System.Drawing.Drawing2D.GraphicsPath();
50 | myGraphicsPath.Reset();
51 | //添家多边形点,绘制出路径
52 | myGraphicsPath.AddPolygon(points);
53 | Region myRegion = new Region();
54 | myRegion.MakeEmpty();
55 | //获得交集
56 | myRegion.Union(myGraphicsPath);
57 | //返回判断点是否在多边形里
58 | return myRegion.IsVisible(point);
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CanvasDemo.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 一个强类型的资源类,用于查找本地化的字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// 返回此类使用的缓存的 ResourceManager 实例。
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CanvasDemo.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 重写当前线程的 CurrentUICulture 属性,对
51 | /// 使用此强类型资源类的所有资源查找执行重写。
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Painter/CubeElement.cs:
--------------------------------------------------------------------------------
1 | using CanvasDemo.Canvas;
2 | using CanvasDemo.Data;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.Drawing.Imaging;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using System.Windows.Forms;
12 |
13 | namespace CanvasDemo.Painter
14 | {
15 | public class CubeElement : ObjElement, IToolTipElement
16 | {
17 |
18 |
19 | public CubeElement(CubeLayer layer, ElementData data, int sideLength) : base(layer, data)
20 | {
21 | this.Rect.Width = sideLength;
22 | this.Rect.Height = sideLength;
23 | }
24 |
25 | public bool IsHover { get; set; } = false;
26 |
27 | public static Brush FillBrush = new SolidBrush(Color.Blue);
28 | public static Brush SelectBrush = new SolidBrush(Color.Green);
29 |
30 | public static Brush ErrorBrush = new SolidBrush(Color.Red);
31 |
32 |
33 |
34 | public override void Drawing(Graphics g)
35 | {
36 |
37 | var titleH = (int)(Rect.Height * 0.25);
38 |
39 | //选择和错误使用不同的颜色
40 | var fillBrush = FillBrush;
41 | if (Data.IsError)
42 | fillBrush = ErrorBrush;
43 | else if (IsSelected)
44 | fillBrush = SelectBrush;
45 |
46 | if (titleH * Viewer.Zoom > 10)//如果标题大于10就认真绘制,如哦小于那么就简化
47 | {
48 | var borderW = (int)(Rect.Height * 0.01 * Viewer.Zoom) + 1;
49 |
50 | g.FillRectangle(Brushes.White, Viewer.LocalToShow(Rect.X, Rect.Y, Rect.Width, titleH + 1));
51 | var fontSize = (int)(titleH / 2 * Viewer.Zoom) + 1;
52 | if (fontSize >= 3)
53 | {
54 | g.DrawString(Data.Title.ToString(), new Font("微软雅黑", fontSize > 60 ? 60 : fontSize), Brushes.Black, Viewer.LocalToShow(Rect.X + (int)(borderW / Viewer.Zoom), Rect.Y + (int)(borderW / Viewer.Zoom), Rect.Width, Rect.Height));
55 | }
56 |
57 | var contentRect = Viewer.LocalToShow(Rect.X, Rect.Y + titleH, Rect.Width, Rect.Height - titleH);
58 | g.FillRectangle(fillBrush, contentRect);
59 | g.DrawString(Data.Group.ToString(), new Font("微软雅黑", (Rect.Height - titleH) / 2 * Viewer.Zoom),
60 | Brushes.White, contentRect, SFAlignment);
61 |
62 |
63 | if (IsHover)
64 | g.DrawRectangle(new Pen(Brushes.Red, borderW * 2), Viewer.LocalToShow(Rect));
65 | else
66 | g.DrawRectangle(new Pen(Brushes.Black, borderW), Viewer.LocalToShow(Rect));
67 |
68 | }
69 | else if (titleH * Viewer.Zoom > 5)
70 | {
71 | g.FillRectangle(fillBrush, Viewer.LocalToShow(Rect));
72 |
73 | var fontSize = (int)(titleH * Viewer.Zoom) + 1;
74 | if (fontSize >= 3)
75 | {
76 | g.DrawString(Data.Group.ToString(), new Font("微软雅黑", fontSize > 60 ? 60 : fontSize), Brushes.White, Viewer.LocalToShow(Rect.X + 1, Rect.Y + 1, Rect.Width, Rect.Height), SFAlignment);
77 | }
78 | }
79 | else
80 | {
81 | g.FillRectangle(fillBrush, Viewer.LocalToShow(Rect));
82 | }
83 | }
84 |
85 | public override void DrawingAfter(Graphics g)
86 | {
87 |
88 | }
89 |
90 | public string GetToolTipTitle()
91 | {
92 | return $"[{Data.Group}] {Data.Title}";
93 | }
94 |
95 | protected static StringFormat SFAlignment = new StringFormat()
96 | {
97 | Alignment = StringAlignment.Center,
98 | LineAlignment = StringAlignment.Center,
99 | };
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Form1.cs:
--------------------------------------------------------------------------------
1 | using CanvasDemo.Canvas;
2 | using CanvasDemo.Data;
3 | using CanvasDemo.Painter;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Data;
8 | using System.Drawing;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 | using System.Windows.Forms;
13 |
14 | namespace CanvasDemo
15 | {
16 | public partial class Form1 : Form
17 | {
18 | public Form1()
19 | {
20 | InitializeComponent();
21 | timCanvas1.Initialize();
22 | }
23 |
24 | private void timer1_Tick(object sender, EventArgs e)
25 | {
26 | var r = new Random().Next(20);
27 | CubeDatas.ForEach(x => x.IsError = x.Group == r);
28 | timCanvas1.Refresh();
29 | }
30 |
31 | EllipseLayer EllipseLayer;
32 |
33 |
34 | List CubeDatas = new();
35 |
36 | CubeLayer CubeLayer;
37 |
38 |
39 |
40 | private void cInit_Click(object sender, EventArgs e)
41 | {
42 | var r = new Random();
43 |
44 | EllipseLayer = new EllipseLayer(timCanvas1);
45 | EllipseLayer.IsVisible = false;
46 | for (int x = 0; x < 100; x++)
47 | {
48 | for (int y = 0; y < 50; y++)
49 | {
50 | var cubeData = new ElementData()
51 | {
52 | ID = Guid.NewGuid().ToString(),
53 | Group = r.Next(20),
54 | Title = "E-" + (x + y * 100).ToString(),
55 | };
56 | var elem = new EllipseElement(EllipseLayer, cubeData, 50);
57 | elem.Rect.X = x * 60;
58 | elem.Rect.Y = y * 60;
59 | }
60 | }
61 | timCanvas1.SetCurrentLayer(CubeLayer);
62 |
63 |
64 | CubeLayer = new CubeLayer(timCanvas1);
65 | for (int x = 0; x < 300; x++)
66 | {
67 | for (int y = 0; y < 200; y++)
68 | {
69 | var cubeData = new ElementData()
70 | {
71 | ID = Guid.NewGuid().ToString(),
72 | Group = r.Next(20),
73 | Title = (x + y * 200).ToString(),
74 | };
75 | CubeDatas.Add(cubeData);
76 | var elem = new CubeElement(CubeLayer, cubeData, 100);
77 | elem.Rect.X = x * 120;
78 | elem.Rect.Y = y * 150;
79 | }
80 | }
81 | timCanvas1.SetCurrentLayer(CubeLayer);
82 |
83 |
84 | timCanvas1.Refresh();
85 | }
86 |
87 | private void cLayer1_Click(object sender, EventArgs e)
88 | {
89 | cLayer1.Checked = !cLayer1.Checked;
90 | CubeLayer.IsVisible = cLayer1.Checked;
91 | timCanvas1.Refresh();
92 | }
93 |
94 | private void cFocusBtn_Click(object sender, EventArgs e)
95 | {
96 | var elem = CubeLayer.Elements.FirstOrDefault(x => ((CubeElement)x).Data.Title == cFocusText.Text);
97 | if (elem == null) return;
98 |
99 | timCanvas1.SetElementFocus(elem);
100 | timCanvas1.Refresh();
101 | }
102 |
103 | private void cLayer2_Click(object sender, EventArgs e)
104 | {
105 | cLayer2.Checked = !cLayer2.Checked;
106 | EllipseLayer.IsVisible = cLayer2.Checked;
107 | timCanvas1.Refresh();
108 | }
109 |
110 | private void cState_Click(object sender, EventArgs e)
111 | {
112 | timer1.Start();
113 | }
114 |
115 | private void toolStripButton1_Click(object sender, EventArgs e)
116 | {
117 | timCanvas1.ToolTip = new ToolTipComponent(timCanvas1);
118 |
119 | }
120 |
121 | private void cSetText_Click(object sender, EventArgs e)
122 | {
123 | timCanvas1.ElementEditor.SelectedElements.ForEach(x =>
124 | {
125 | if (x is CubeElement cube)
126 | {
127 | cube.Data.Title = cText.Text;
128 | }
129 | });
130 | timCanvas1.Refresh();
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/SelectionBox.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 |
9 | namespace CanvasDemo.Canvas
10 | {
11 | public class SelectionBox : Element
12 | {
13 | ///
14 | /// 是否从左往右选择
15 | ///
16 | public bool IsLeftToRight { get; set; } = true;
17 |
18 | public bool SelectionBoxIsShow { get; set; } = false;
19 |
20 | //框选开始坐标
21 | Point Start;
22 | //鼠标中键按下
23 | bool IsMouseLeftDown = false;
24 |
25 | ElementEditor Editor;
26 |
27 | public SelectionBox(ElementEditor editor, TimCanvas canvas) : base(canvas,nameof(SelectionBox))
28 | {
29 | Editor = editor;
30 | }
31 |
32 | public override void Drawing(Graphics g)
33 | {
34 | if (SelectionBoxIsShow == true)
35 | {
36 | if (IsLeftToRight == true)
37 | {
38 | g.FillRectangle(new SolidBrush(Color.FromArgb(100, 51, 153, 255)), Viewer.LocalToShow(Rect));
39 | g.DrawRectangle(new Pen(Color.FromArgb(255, 51, 153, 255)), Viewer.LocalToShow(Rect));
40 | }
41 | else
42 | {
43 | g.FillRectangle(new SolidBrush(Color.FromArgb(100, 153, 255, 51)), Viewer.LocalToShow(Rect));
44 | g.DrawRectangle(new Pen(Color.FromArgb(255, 153, 255, 51)), Viewer.LocalToShow(Rect));
45 | }
46 |
47 | }
48 | }
49 |
50 | public void MouseDown(MouseEventArgs e)
51 | {
52 | if (e.Button == MouseButtons.Left)
53 | {
54 | IsMouseLeftDown = true;
55 | Start = Viewer.MousePointToLocal(e.Location);
56 | Rect.Width = 0;
57 | Rect.Height = 0;
58 | SelectionBoxIsShow = true;
59 | }
60 | }
61 |
62 | public void MouseMove(MouseEventArgs e)
63 | {
64 | //比例缩放后结束坐标也要做调整
65 | if (IsMouseLeftDown == true)
66 | {
67 | var end = Viewer.MousePointToLocal(e.Location);
68 |
69 | IsLeftToRight = Start.X < end.X;
70 |
71 | Rect.X = Start.X < end.X ? Start.X : end.X;
72 | Rect.Y = Start.Y < end.Y ? Start.Y : end.Y;
73 |
74 | Rect.Width = Math.Abs(Start.X - end.X);
75 | Rect.Height = Math.Abs(Start.Y - end.Y);
76 | }
77 | }
78 |
79 | public void MouseUp(MouseEventArgs e)
80 | {
81 | if (e.Button == MouseButtons.Left)
82 | {
83 | IsMouseLeftDown = false;
84 | SelectionBoxIsShow = false;
85 |
86 | var end = Viewer.MousePointToLocal(e.Location);
87 | if (end.Distance(Start) < 15)
88 | {//开始和结束距离短,认为是鼠标点击选择
89 | PointSelectOver(e.Location);
90 | }
91 | else
92 | {
93 | BoxSelectOver();
94 | }
95 |
96 | }
97 | }
98 |
99 | public void MouseWheel(MouseEventArgs e)
100 | {
101 |
102 | }
103 |
104 | ///
105 | /// 选择单个对象
106 | ///
107 | private void PointSelectOver(Point mousePoint)
108 | {
109 | if (Control.ModifierKeys != Keys.Control)
110 | {
111 | Editor.ClearSelected();
112 | }
113 | var point = Viewer.MousePointToLocal(mousePoint);
114 | foreach (var item in Canvas.Layers)
115 | {
116 | if (item.IsActive == false) continue;
117 | var elm = item.Elements.FirstOrDefault(x => x.Rect.Contains(point) == true);
118 | if (elm != null)
119 | {
120 |
121 | Editor.AddSelected(new List() { elm });
122 | Editor.SetCurrent(elm);
123 | return;
124 | }
125 | }
126 |
127 | }
128 |
129 | ///
130 | /// 选择被框选的对象
131 | ///
132 | private void BoxSelectOver()
133 | {
134 | if (Control.ModifierKeys != Keys.Control)
135 | {
136 | //撤销以前的选择
137 | Editor.ClearSelected();
138 | }
139 | foreach (var item in Canvas.Layers)
140 | {
141 | if (item.IsActive == false) continue;
142 |
143 | if (IsLeftToRight == true)
144 | {//全部选中才算选中
145 | Editor.AddSelected(item.Elements.AsParallel().Where(x => Rect.Contains(x.Rect) == true).ToList());
146 | }
147 | else
148 | {//相交就认为已经选中
149 | Editor.AddSelected(item.Elements.AsParallel().Where(x => x.Rect.IntersectsWith(Rect) == true).ToList());
150 | }
151 | }
152 | Editor.SetCurrent(Editor.SelectedElements.FirstOrDefault());
153 | }
154 |
155 | public override void DrawingAfter(Graphics g)
156 | {
157 |
158 | }
159 |
160 |
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/ObjectElement.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CanvasDemo.Canvas
9 | {
10 | ///
11 | /// 对象元素
12 | ///
13 | public abstract class ObjElement : ObjElement where T : IElementData
14 | {
15 | ///
16 | /// 对象实体
17 | ///
18 | public T Data { get; set; }
19 |
20 | public ObjElement(Layer layer, T data) : base(layer, data.ID)
21 | {
22 | Data = data;
23 | }
24 | }
25 |
26 | public abstract class ObjElement : Element
27 | {
28 |
29 | public Layer Layer;
30 |
31 | public ObjElement(Layer layer, string id) : base(layer.Canvas, id)
32 | {
33 | Layer = layer;
34 | Layer.Elements.Add(this);
35 | }
36 |
37 | ///
38 | /// 是否是选中
39 | ///
40 | public bool IsSelected { get; private set; } = false;
41 |
42 | ///
43 | /// 是否是当前对象
44 | ///
45 | public bool IsCurrent { get; private set; } = false;
46 |
47 | ///
48 | /// 选择对象
49 | ///
50 | public void Selected()
51 | {
52 | IsSelected = true;
53 | SelectedEvent();
54 | }
55 | protected virtual void SelectedEvent() { }
56 |
57 | ///
58 | /// 清除对象选择
59 | ///
60 | public void UnSelected()
61 | {
62 | IsSelected = false;
63 | UnSelectedEvent();
64 | }
65 | protected virtual void UnSelectedEvent() { }
66 |
67 | public void Current()
68 | {
69 | IsCurrent = true;
70 | CurrentEvent();
71 | }
72 | protected virtual void CurrentEvent() { }
73 |
74 | public void UnCurrent()
75 | {
76 | IsCurrent = false;
77 | UnCurrentEvent();
78 | }
79 | protected virtual void UnCurrentEvent() { }
80 |
81 |
82 | public int JoystickSize { get { return (Rect.Width + Rect.Height) / 20 + 1; } }
83 |
84 | static Brush JoystickCurrent = new SolidBrush(Color.FromArgb(230, 255, 255, 255));
85 | static Brush JoystickSelect = new SolidBrush(Color.FromArgb(230, 50, 50, 50));
86 |
87 | ///
88 | /// 绘制八个操纵柄
89 | ///
90 | ///
91 | public void DrawingJoystick(Graphics g)
92 | {
93 | if (IsSelected == false) return;
94 |
95 | var cX = Rect.Width / 2;
96 | var cY = Rect.Height / 2;
97 | var s = JoystickSize;
98 | if (IsCurrent == false)
99 | {
100 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X, Rect.Y + (0), s, s));
101 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (cX - s / 2), Rect.Y + (0), s, s));
102 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (0), s, s));
103 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (cY - s / 2), s, s));
104 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (Rect.Height - s), s, s));
105 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (cX - s / 2), Rect.Y + (Rect.Height - s), s, s));
106 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (0), Rect.Y + (Rect.Height - s), s, s));
107 | g.FillRectangle(JoystickSelect, Viewer.LocalToShow(Rect.X + (0), Rect.Y + (cY - s / 2), s, s));
108 | }
109 | else if (IsCurrent == true)
110 | {
111 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X, Rect.Y + (0), s, s));
112 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (cX - s / 2), Rect.Y + (0), s, s));
113 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (0), s, s));
114 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (cY - s / 2), s, s));
115 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (Rect.Height - s), s, s));
116 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (cX - s / 2), Rect.Y + (Rect.Height - s), s, s));
117 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (0), Rect.Y + (Rect.Height - s), s, s));
118 | g.FillRectangle(JoystickCurrent, Viewer.LocalToShow(Rect.X + (0), Rect.Y + (cY - s / 2), s, s));
119 |
120 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X, Rect.Y + (0), s, s));
121 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (cX - s / 2), Rect.Y + (0), s, s));
122 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (0), s, s));
123 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (cY - s / 2), s, s));
124 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (Rect.Width - s), Rect.Y + (Rect.Height - s), s, s));
125 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (cX - s / 2), Rect.Y + (Rect.Height - s), s, s));
126 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (0), Rect.Y + (Rect.Height - s), s, s));
127 | g.DrawRectangle(Pens.Black, Viewer.LocalToShow(Rect.X + (0), Rect.Y + (cY - s / 2), s, s));
128 |
129 | }
130 | }
131 |
132 |
133 |
134 | }
135 |
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/Canvas.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/ClassDiagram1.cd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | AAAAQAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAIAAAAA=
7 | Canvas\Backgrounder.cs
8 |
9 |
10 |
11 |
12 |
13 | QCBBDBAAIDAAUQAAGCQAIAgAAAAgiEAEgBAoggAIgAQ=
14 | Canvas\Canvas.cs
15 |
16 |
17 |
18 |
19 |
20 |
21 | AAAAAAAAAAAAgAAAAAIAAAAAACAAgAAAAAAAAIAAAAI=
22 | Canvas\Element.cs
23 |
24 |
25 |
26 |
27 |
28 | AAkQpAAAAEAAAEIAAAgAEwAFAABoEAACAIAQAICQABc=
29 | Canvas\ElementEditor.cs
30 |
31 |
32 |
33 |
34 |
35 | AKAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAIAAAAI=
36 | Canvas\FocusElement.cs
37 |
38 |
39 |
40 |
41 |
42 | AAAAAAAAAAAAEEAAEAIQAAQAAAAEEAAAAIAAAICAAAI=
43 | Canvas\Layer.cs
44 |
45 |
46 |
47 |
48 |
49 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAA=
50 | Canvas\ObjectElement.cs
51 |
52 |
53 |
54 |
55 |
56 | AAAAAAAQAgAAUAgAAACAUAAAAAAAAAAEAgAgBAAAAgo=
57 | Canvas\ObjectElement.cs
58 |
59 |
60 |
61 |
62 |
63 | AAAAAAAAAAAAAAAAAAACAAAAAACAAABAAAAAAAAAAAA=
64 | Canvas\PointExtension.cs
65 |
66 |
67 |
68 |
69 |
70 | AAkEAAAAACQAAEAAQAAAAAAQAAAAEAAAAIAAAICAAAI=
71 | Canvas\SelectionBox.cs
72 |
73 |
74 |
75 |
76 |
77 | ABAAEAAABAAEAECA0AIAAAAARQDAEAAABIAAAACAAQA=
78 | Canvas\Viewer.cs
79 |
80 |
81 |
82 |
83 |
84 | AAAAAEAAAAAAgEAAAAAAAAAAAAABAAAAAAAAAAAAAAA=
85 | Data\ElementData.cs
86 |
87 |
88 |
89 |
90 |
91 |
92 | AAgAAAAAAAAAAAAAgAAAAACAAAAAAAAAAAAICIAAAAI=
93 | Painter\CubeElement.cs
94 |
95 |
96 |
97 |
98 |
99 |
100 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAI=
101 | Painter\CubeLayer.cs
102 |
103 |
104 |
105 |
106 |
107 | AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAI=
108 | Painter\EllipseElement.cs
109 |
110 |
111 |
112 |
113 |
114 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAI=
115 | Painter\EllipseLayer.cs
116 |
117 |
118 |
119 |
120 |
121 | AAAAAAAAAAAAAAAAEAIAAAAAAAAEAAAAAAIAAIAAAAI=
122 | Painter\ToolTipComponent.cs
123 |
124 |
125 |
126 |
127 |
128 |
129 | AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
130 | Canvas\IElementData.cs
131 |
132 |
133 |
134 |
135 |
136 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAA=
137 | Canvas\IToolTipElement.cs
138 |
139 |
140 |
141 |
142 |
143 | AAAAAAAAAAAAAAAAEAAAAAAAAAAEAAAAAAAAAIAAAAI=
144 | Canvas\IToolTipElement.cs
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Form1.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | text/microsoft-resx
50 |
51 |
52 | 2.0
53 |
54 |
55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
56 |
57 |
58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
59 |
60 |
61 | 17, 17
62 |
63 |
64 |
65 |
66 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
67 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
68 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
69 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
70 | AAAAAElFTkSuQmCC
71 |
72 |
73 |
74 |
75 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
76 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
77 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
78 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
79 | AAAAAElFTkSuQmCC
80 |
81 |
82 |
83 |
84 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
85 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
86 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
87 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
88 | AAAAAElFTkSuQmCC
89 |
90 |
91 |
92 |
93 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
94 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
95 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
96 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
97 | AAAAAElFTkSuQmCC
98 |
99 |
100 |
101 |
102 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
103 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
104 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
105 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
106 | AAAAAElFTkSuQmCC
107 |
108 |
109 |
110 |
111 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
112 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
113 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
114 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
115 | AAAAAElFTkSuQmCC
116 |
117 |
118 |
119 |
120 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
121 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACRSURBVDhPY/j27dt/SjDYACcnJ7IwigEf3n8kCZNswPNb
122 | J/+f6DYF0yA+yQac6Db5f6hWCmwIiE+mC0wIu2DS2Vf/F1x6DefjwlgNyNr34r/0wkdgTMgQDAOQNRNj
123 | CIoBOg0rMTTDMLIhIHbriZeYBmDTiIxBGkEYxge5liQDsGGQqykyAISpZwAlmIEywMAAAAc1/Jwvt6sN
124 | AAAAAElFTkSuQmCC
125 |
126 |
127 |
128 | 127, 17
129 |
130 |
131 | 217, 17
132 |
133 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/Viewer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 |
9 | namespace CanvasDemo.Canvas
10 | {
11 | public class Viewer
12 | {
13 | TimCanvas Canvas;
14 | public Viewer(TimCanvas canvas)
15 | {
16 | Canvas = canvas;
17 |
18 | //默认图纸坐标
19 | Zero = new Point(Canvas.Width / 2, Canvas.Height / 2);
20 | Viewport = new Rectangle(0 - Zero.X, 0 - Zero.Y, Canvas.Width, Canvas.Height);
21 |
22 | }
23 |
24 | ///
25 | /// 零点坐标(默认为画板中间)
26 | ///
27 | public Point Zero;//不能用属性,不然没法使用Offset之类函数
28 |
29 | ///
30 | /// 视口,当前用户可以看到的区域
31 | ///
32 | public Rectangle Viewport;//不能用属性,不然没法使用Offset之类函数
33 |
34 | // int DebugVX = 50, DebugVL = 100;//调试时故意减少视口,用于调试
35 |
36 | //缩放比例
37 | public float Zoom = 1;
38 |
39 | //最小比例
40 | private float MinZoom = 0.01f;
41 | //最大比例
42 | private float MaxZoom = 100;
43 |
44 | #region 视图调整
45 |
46 | //鼠标中键按下
47 | bool IsMouseMiddleDown = false;
48 |
49 | ///
50 | /// 移动前鼠标位置
51 | ///
52 | Point OldMousePoint;
53 |
54 | public void MouseDown(MouseEventArgs e)
55 | {
56 | if (e.Button == MouseButtons.Middle) IsMouseMiddleDown = true;
57 | OldMousePoint = e.Location;
58 | }
59 |
60 | public void MouseUp(MouseEventArgs e)
61 | {
62 | if (e.Button == MouseButtons.Middle) IsMouseMiddleDown = false;
63 | }
64 |
65 |
66 | public void MouseMove(MouseEventArgs e)
67 | {
68 | var newLocation = e.Location;
69 |
70 | if (IsMouseMiddleDown == true)
71 | {//鼠标中键移动图纸
72 |
73 | var x = (newLocation.X - OldMousePoint.X);
74 | var y = (newLocation.Y - OldMousePoint.Y);
75 |
76 | Zero.Offset(x, y);
77 |
78 | Viewport.X = (int)((0 - Zero.X) / Zoom);
79 | Viewport.Y = (int)((0 - Zero.Y) / Zoom);
80 | }
81 |
82 | OldMousePoint = newLocation;
83 | }
84 |
85 | public void MouseWheel(MouseEventArgs e)
86 | {
87 | //鼠标滚轮滚动图纸
88 | int tZeroX = 0;
89 | int tZeroY = 0;
90 | if (e.Delta > 0)
91 | {
92 | if (Zoom == MaxZoom) return;
93 |
94 | Zoom = Zoom * 1.25f;
95 | if (Zoom > MaxZoom) Zoom = MaxZoom;
96 |
97 | tZeroX = (int)((e.X - Zero.X) - (e.X - Zero.X) * 1.25f);
98 | tZeroY = (int)((e.Y - Zero.Y) - (e.Y - Zero.Y) * 1.25f);
99 | }
100 | else
101 | {
102 | if (Zoom == MinZoom) return;
103 |
104 | Zoom = Zoom * 0.8f;
105 | if (Zoom < MinZoom) Zoom = MinZoom;
106 |
107 | tZeroX = (int)((e.X - Zero.X) - (e.X - Zero.X) * 0.8f);
108 | tZeroY = (int)((e.Y - Zero.Y) - (e.Y - Zero.Y) * 0.8f);
109 | }
110 |
111 | //调整相对坐标位置
112 | Zero.Offset(tZeroX, tZeroY);
113 |
114 | //调整视口位置
115 | Viewport.X = (int)((0 - Zero.X) / Zoom);
116 | Viewport.Y = (int)((0 - Zero.Y) / Zoom);
117 | Viewport.Width = (int)((Canvas.Width) / Zoom);
118 | Viewport.Height = (int)((Canvas.Height) / Zoom);
119 | }
120 |
121 | ///
122 | /// 设置缩放
123 | ///
124 | ///
125 | public void SetZoom(float zoom)
126 | {
127 | Zoom = zoom;
128 | Canvas.Refresh();
129 | }
130 | public void SetZero(int x,int y)
131 | {
132 | Zero.X = x;
133 | Zero.Y = y;
134 | //调整视口位置
135 | Viewport.X = (int)((0 - Zero.X) / Zoom);
136 | Viewport.Y = (int)((0 - Zero.Y) / Zoom);
137 | }
138 |
139 |
140 | ///
141 | /// 设置成完整显示
142 | ///
143 | public void SetFullDisplay()
144 | {
145 | var w = (float)Canvas.Width / (float)Canvas.BackgrounderSize.Width;
146 | var h = (float)Canvas.Height / (float)Canvas.BackgrounderSize.Height;
147 | Zoom = w < h ? w : h;
148 | MinZoom = Zoom;
149 | MaxZoom = Zoom * 100;
150 |
151 | Zero.X = Canvas.Width / 2;
152 | Zero.Y = Canvas.Height / 2;
153 |
154 | Viewport.X = (int)((0 - Zero.X) / Zoom);
155 | Viewport.Y = (int)((0 - Zero.Y) / Zoom);
156 | Viewport.Width = (int)(Canvas.Width / Zoom);
157 | Viewport.Height = (int)(Canvas.Height / Zoom);
158 |
159 | Canvas.Refresh();
160 | }
161 |
162 | #endregion
163 |
164 | #region 控制是否绘制,用于优化性能
165 |
166 | ///
167 | /// 是否在区域中用于优化性能,这个涉及到性能优化
168 | ///
169 | ///
170 | ///
171 | public bool IsInZone(Element element)
172 | {
173 | return Viewport.IntersectsWith(element.Rect);
174 | }
175 |
176 | public bool IsInZone(Rectangle rect)
177 | {
178 | return Viewport.IntersectsWith(rect);
179 | }
180 |
181 | #endregion
182 |
183 | #region 本地坐标转世界坐标
184 |
185 | ///
186 | /// 本地(图纸)矩形变换到显示矩形
187 | ///
188 | public Rectangle LocalToShow(Rectangle rect)
189 | {
190 | //要清晰的确定本地和世界的关系
191 | var r = new Rectangle(rect.Location, rect.Size);
192 |
193 | //计算缩放坐标
194 | r.X = (int)(r.X * Zoom) + Zero.X;
195 | r.Y = (int)(r.Y * Zoom) + Zero.Y;
196 |
197 | //调整矩形大小
198 | r.Width = (int)Math.Round(rect.Width * Zoom, 0);
199 | r.Height = (int)Math.Round(rect.Height * Zoom, 0);
200 |
201 | return r;
202 | }
203 |
204 | public Point LocalToShow(Point point)
205 | {
206 | return new Point((int)(point.X * Zoom) + Zero.X, (int)(point.Y * Zoom) + Zero.Y);
207 | }
208 |
209 | public Rectangle LocalToShow(int x, int y, int width, int height)
210 | {
211 | return new Rectangle(
212 | (int)(x * Zoom) + Zero.X,
213 | (int)(y * Zoom) + Zero.Y,
214 | (int)Math.Round(width * Zoom, 0),
215 | (int)Math.Round(height * Zoom, 0)
216 | );
217 | }
218 |
219 | public int ToShowX(int x)
220 | {
221 | return (int)(x * Zoom) + Zero.X;
222 | }
223 | public int ToShowY(int y)
224 | {
225 | return (int)(y * Zoom) + Zero.Y;
226 | }
227 |
228 | ///
229 | /// 本地尺寸变换到显示的尺寸
230 | ///
231 | ///
232 | ///
233 | public Size LocalToShow(Size size)
234 | {
235 | return new Size((int)Math.Round(size.Width * Zoom, 0), (int)Math.Round(size.Height * Zoom, 0));
236 | }
237 |
238 | ///
239 | /// 鼠标坐标点变换到显示坐标点
240 | ///
241 | ///
242 | ///
243 | public Point MousePointToLocal(Point point)
244 | {
245 | return new Point((int)Math.Round((point.X - Zero.X) / Zoom, 0), (int)Math.Round((point.Y - Zero.Y) / Zoom, 0));
246 | }
247 |
248 | #endregion
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Form1.Designer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace CanvasDemo
3 | {
4 | partial class Form1
5 | {
6 | ///
7 | /// Required designer variable.
8 | ///
9 | private System.ComponentModel.IContainer components = null;
10 |
11 | ///
12 | /// Clean up any resources being used.
13 | ///
14 | /// true if managed resources should be disposed; otherwise, false.
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (disposing && (components != null))
18 | {
19 | components.Dispose();
20 | }
21 | base.Dispose(disposing);
22 | }
23 |
24 | #region Windows Form Designer generated code
25 |
26 | ///
27 | /// Required method for Designer support - do not modify
28 | /// the contents of this method with the code editor.
29 | ///
30 | private void InitializeComponent()
31 | {
32 | components = new System.ComponentModel.Container();
33 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
34 | toolStrip1 = new System.Windows.Forms.ToolStrip();
35 | cInit = new System.Windows.Forms.ToolStripButton();
36 | toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
37 | cLayer1 = new System.Windows.Forms.ToolStripButton();
38 | cLayer2 = new System.Windows.Forms.ToolStripButton();
39 | toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
40 | cFocusText = new System.Windows.Forms.ToolStripTextBox();
41 | cFocusBtn = new System.Windows.Forms.ToolStripButton();
42 | toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
43 | cState = new System.Windows.Forms.ToolStripButton();
44 | toolStripButton1 = new System.Windows.Forms.ToolStripButton();
45 | toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
46 | cText = new System.Windows.Forms.ToolStripTextBox();
47 | cSetText = new System.Windows.Forms.ToolStripButton();
48 | timer1 = new System.Windows.Forms.Timer(components);
49 | timCanvas1 = new Canvas.TimCanvas();
50 | toolTip1 = new System.Windows.Forms.ToolTip(components);
51 | toolStrip1.SuspendLayout();
52 | SuspendLayout();
53 | //
54 | // toolStrip1
55 | //
56 | toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { cInit, toolStripSeparator4, cLayer1, cLayer2, toolStripSeparator1, cFocusText, cFocusBtn, toolStripSeparator2, cState, toolStripButton1, toolStripSeparator3, cText, cSetText });
57 | toolStrip1.Location = new System.Drawing.Point(0, 0);
58 | toolStrip1.Name = "toolStrip1";
59 | toolStrip1.Size = new System.Drawing.Size(1036, 25);
60 | toolStrip1.TabIndex = 0;
61 | toolStrip1.Text = "toolStrip1";
62 | //
63 | // cInit
64 | //
65 | cInit.Image = (System.Drawing.Image)resources.GetObject("cInit.Image");
66 | cInit.ImageTransparentColor = System.Drawing.Color.Magenta;
67 | cInit.Name = "cInit";
68 | cInit.Size = new System.Drawing.Size(76, 22);
69 | cInit.Text = "初始数据";
70 | cInit.Click += cInit_Click;
71 | //
72 | // toolStripSeparator4
73 | //
74 | toolStripSeparator4.Name = "toolStripSeparator4";
75 | toolStripSeparator4.Size = new System.Drawing.Size(6, 25);
76 | //
77 | // cLayer1
78 | //
79 | cLayer1.Checked = true;
80 | cLayer1.CheckState = System.Windows.Forms.CheckState.Checked;
81 | cLayer1.Image = (System.Drawing.Image)resources.GetObject("cLayer1.Image");
82 | cLayer1.ImageTransparentColor = System.Drawing.Color.Magenta;
83 | cLayer1.Name = "cLayer1";
84 | cLayer1.Size = new System.Drawing.Size(76, 22);
85 | cLayer1.Text = "方块图层";
86 | cLayer1.Click += cLayer1_Click;
87 | //
88 | // cLayer2
89 | //
90 | cLayer2.Image = (System.Drawing.Image)resources.GetObject("cLayer2.Image");
91 | cLayer2.ImageTransparentColor = System.Drawing.Color.Magenta;
92 | cLayer2.Name = "cLayer2";
93 | cLayer2.Size = new System.Drawing.Size(76, 22);
94 | cLayer2.Text = "圆形图层";
95 | cLayer2.Click += cLayer2_Click;
96 | //
97 | // toolStripSeparator1
98 | //
99 | toolStripSeparator1.Name = "toolStripSeparator1";
100 | toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
101 | //
102 | // cFocusText
103 | //
104 | cFocusText.Name = "cFocusText";
105 | cFocusText.Size = new System.Drawing.Size(100, 25);
106 | //
107 | // cFocusBtn
108 | //
109 | cFocusBtn.Image = (System.Drawing.Image)resources.GetObject("cFocusBtn.Image");
110 | cFocusBtn.ImageTransparentColor = System.Drawing.Color.Magenta;
111 | cFocusBtn.Name = "cFocusBtn";
112 | cFocusBtn.Size = new System.Drawing.Size(52, 22);
113 | cFocusBtn.Text = "定位";
114 | cFocusBtn.Click += cFocusBtn_Click;
115 | //
116 | // toolStripSeparator2
117 | //
118 | toolStripSeparator2.Name = "toolStripSeparator2";
119 | toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
120 | //
121 | // cState
122 | //
123 | cState.Image = (System.Drawing.Image)resources.GetObject("cState.Image");
124 | cState.ImageTransparentColor = System.Drawing.Color.Magenta;
125 | cState.Name = "cState";
126 | cState.Size = new System.Drawing.Size(76, 22);
127 | cState.Text = "显示状态";
128 | cState.Click += cState_Click;
129 | //
130 | // toolStripButton1
131 | //
132 | toolStripButton1.Image = (System.Drawing.Image)resources.GetObject("toolStripButton1.Image");
133 | toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
134 | toolStripButton1.Name = "toolStripButton1";
135 | toolStripButton1.Size = new System.Drawing.Size(76, 22);
136 | toolStripButton1.Text = "悬停高亮";
137 | toolStripButton1.Click += toolStripButton1_Click;
138 | //
139 | // toolStripSeparator3
140 | //
141 | toolStripSeparator3.Name = "toolStripSeparator3";
142 | toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
143 | //
144 | // cText
145 | //
146 | cText.Name = "cText";
147 | cText.Size = new System.Drawing.Size(100, 25);
148 | //
149 | // cSetText
150 | //
151 | cSetText.Image = (System.Drawing.Image)resources.GetObject("cSetText.Image");
152 | cSetText.ImageTransparentColor = System.Drawing.Color.Magenta;
153 | cSetText.Name = "cSetText";
154 | cSetText.Size = new System.Drawing.Size(76, 22);
155 | cSetText.Text = "设置标题";
156 | cSetText.Click += cSetText_Click;
157 | //
158 | // timer1
159 | //
160 | timer1.Interval = 2000;
161 | timer1.Tick += timer1_Tick;
162 | //
163 | // timCanvas1
164 | //
165 | timCanvas1.AllowDrop = true;
166 | timCanvas1.Backgrounder = null;
167 | timCanvas1.CurrentLayer = null;
168 | timCanvas1.Dock = System.Windows.Forms.DockStyle.Fill;
169 | timCanvas1.ElementEditor = null;
170 | timCanvas1.IsLocked = false;
171 | timCanvas1.IsRootFormActivated = true;
172 | timCanvas1.Location = new System.Drawing.Point(0, 25);
173 | timCanvas1.Name = "timCanvas1";
174 | timCanvas1.Size = new System.Drawing.Size(1036, 708);
175 | timCanvas1.TabIndex = 1;
176 | timCanvas1.TabStop = false;
177 | timCanvas1.Viewer = null;
178 | //
179 | // toolTip1
180 | //
181 | toolTip1.Active = false;
182 | toolTip1.ShowAlways = true;
183 | //
184 | // Form1
185 | //
186 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
187 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
188 | ClientSize = new System.Drawing.Size(1036, 733);
189 | Controls.Add(timCanvas1);
190 | Controls.Add(toolStrip1);
191 | Name = "Form1";
192 | Text = "Form1";
193 | toolStrip1.ResumeLayout(false);
194 | toolStrip1.PerformLayout();
195 | ResumeLayout(false);
196 | PerformLayout();
197 | }
198 |
199 | #endregion
200 |
201 | private System.Windows.Forms.ToolStrip toolStrip1;
202 | private System.Windows.Forms.ToolStripButton cInit;
203 | private System.Windows.Forms.Timer timer1;
204 | private System.Windows.Forms.ToolStripButton cLayer1;
205 | private System.Windows.Forms.ToolStripButton cLayer2;
206 | private Canvas.TimCanvas timCanvas1;
207 | private System.Windows.Forms.ToolTip toolTip1;
208 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
209 | private System.Windows.Forms.ToolStripTextBox cFocusText;
210 | private System.Windows.Forms.ToolStripButton cFocusBtn;
211 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
212 | private System.Windows.Forms.ToolStripButton cState;
213 | private System.Windows.Forms.ToolStripButton toolStripButton1;
214 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
215 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
216 | private System.Windows.Forms.ToolStripTextBox cText;
217 | private System.Windows.Forms.ToolStripButton cSetText;
218 | }
219 | }
220 |
221 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/Canvas.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Drawing;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Forms;
9 |
10 | namespace CanvasDemo.Canvas
11 | {
12 | public delegate void LayerChangedEvent(Layer layer);
13 |
14 | public class TimCanvas : Control, IDisposable
15 | {
16 | public event LayerChangedEvent LayerChanged;
17 |
18 | ///
19 | /// 对象编辑
20 | ///
21 | public ElementEditor ElementEditor { get; set; }
22 |
23 | ///
24 | /// 视图
25 | ///
26 | public Viewer Viewer { get; set; }
27 |
28 | ///
29 | /// 控制背景
30 | ///
31 | public Backgrounder Backgrounder { get; set; }
32 |
33 | public Size BackgrounderSize;//背景图大小,也就是画布大小
34 |
35 | public List Layers = new List();
36 |
37 | ///
38 | /// 当前图层
39 | ///
40 | public Layer CurrentLayer { get; set; }
41 |
42 | ///
43 | /// 是否处于锁定状态
44 | ///
45 | public bool IsLocked { get; set; } = false;
46 |
47 | #region 图纸配置
48 |
49 | ///
50 | /// 是否高质量图纸缩放
51 | ///
52 | public static bool IsHighQualityDrawingScaling { get; set; } = false;
53 |
54 | #endregion
55 |
56 | ///
57 | /// 根窗体激活状态
58 | ///
59 | public bool IsRootFormActivated { get; set; } = true;
60 |
61 |
62 | public TimCanvas()
63 | {
64 | SetStyle(ControlStyles.UserPaint, true);
65 | SetStyle(ControlStyles.AllPaintingInWmPaint, true);
66 | SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
67 | this.DoubleBuffered = true;
68 |
69 | this.AllowDrop = true;
70 |
71 | InitialToolTip();
72 |
73 | }
74 |
75 |
76 | public virtual void Initialize()
77 | {
78 | Viewer = new Viewer(this);
79 | Backgrounder = new Backgrounder(this);
80 |
81 | //初始化功能性图层
82 | ElementEditor = new ElementEditor(this);
83 |
84 |
85 | //增加事件
86 | this.Paint += DrawingBoard_Paint;
87 | this.MouseMove += DrawingBoard_MouseMove;
88 | this.MouseDown += Canvas_MouseDown;
89 | this.MouseUp += Canvas_MouseUp;
90 | this.MouseWheel += Canvas_MouseWheel;
91 | this.MouseDoubleClick += TimCanvas_MouseDoubleClick;
92 |
93 | //监视根窗体的激活状态
94 | var rootForm = FindForm();
95 | rootForm.Activated += (s, e) => IsRootFormActivated = true;
96 | rootForm.Deactivate += (s, e) => IsRootFormActivated = false;
97 | }
98 |
99 |
100 | #region 鼠标事件调用
101 |
102 | private void Canvas_MouseDown(object sender, MouseEventArgs e)
103 | {
104 | this.Focus();
105 | var l = CurrentLayer?.MouseDown(e);
106 | if (l != true)//优先处理图层的鼠标操作,有些操作需要屏蔽
107 | {
108 | Viewer.MouseDown(e);
109 | ElementEditor.MouseDown(e);
110 | }
111 | this.Refresh();
112 | }
113 |
114 | private void Canvas_MouseUp(object sender, MouseEventArgs e)
115 | {
116 | var l = CurrentLayer?.MouseUp(e);
117 | if (l != true)//优先处理图层的鼠标操作,有些操作需要屏蔽
118 | {
119 | Viewer.MouseUp(e);
120 | ElementEditor.MouseUp(e);
121 | }
122 | this.Refresh();
123 | }
124 |
125 |
126 | private void DrawingBoard_MouseMove(object sender, MouseEventArgs e)
127 | {
128 | var l = CurrentLayer?.MouseMove(e);
129 | if (l != true)//优先处理图层的鼠标操作,有些操作需要屏蔽
130 | {
131 | Viewer.MouseMove(e);
132 | ElementEditor.MouseMove(e);
133 | }
134 | if (e.Button != MouseButtons.None)
135 | {//TODO: 增加了刷新条件,尝试减少刷新来优化系统性
136 | this.Refresh();
137 | }
138 | }
139 |
140 | private void Canvas_MouseWheel(object sender, MouseEventArgs e)
141 | {
142 | var l = CurrentLayer?.MouseWheel(e);
143 | if (l != true)//优先处理图层的鼠标操作,有些操作需要屏蔽
144 | {
145 | Viewer.MouseWheel(e);
146 | }
147 | this.Refresh();
148 | }
149 |
150 | private void TimCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
151 | {
152 |
153 | }
154 |
155 | #endregion
156 |
157 | #region 绘图
158 |
159 | ///
160 | /// 绘制画布
161 | ///
162 | private void DrawingBoard_Paint(object sender, PaintEventArgs e)
163 | {
164 |
165 | Backgrounder.Drawing(e.Graphics);
166 |
167 | //绘制各图层信息-正常内容
168 | foreach (var item in Layers)
169 | {
170 | if (item.IsVisible == true) item.Drawing(e.Graphics);
171 | }
172 |
173 | //绘制各图层信息-顶部内容
174 | foreach (var item in Layers)
175 | {
176 | if (item.IsVisible == true) item.DrawingAfter(e.Graphics);
177 | }
178 |
179 | if (FocusElement != null && FocusElement.IsShow == true)
180 | FocusElement.Drawing(e.Graphics);
181 |
182 |
183 | //绘制选择框
184 | ElementEditor.Drawing(e.Graphics);
185 |
186 |
187 | //绘制拖拽对象
188 | if (DragElement != null)
189 | {
190 | DragElement.Rect.Location = Viewer.MousePointToLocal(this.PointToClient(MousePosition));
191 | DragElement?.Drawing(e.Graphics);
192 | }
193 |
194 |
195 | ToolTip?.Drawing(e.Graphics);
196 | }
197 |
198 | #endregion
199 |
200 | public T GetLayer(string name) where T : class
201 | {
202 | return Layers.FirstOrDefault(x => x.Name == name) as T;
203 | }
204 |
205 | #region 拖拽支持
206 |
207 | ///
208 | /// 拖拽对象
209 | ///
210 | public Element DragElement;
211 |
212 | #endregion
213 |
214 |
215 | #region 交互操作
216 |
217 | protected FocusElement FocusElement = null;
218 |
219 | ///
220 | /// 设置焦点对象
221 | ///
222 | ///
223 | ///
224 | public void SetElementFocus(string id, string layoutName)
225 | {
226 | var layout = Layers.FirstOrDefault(x => x.Name == layoutName);
227 | if (layout == null) return;
228 |
229 | var elem = layout.Elements.FirstOrDefault(x => x.ID == id);
230 | if (elem == null) return;
231 |
232 | SetElementFocus(elem);
233 | }
234 | public void SetElementFocus(ObjElement elem)
235 | {
236 | if (elem == null) return;
237 |
238 | var x = (int)(-1 * (elem.Rect.X + elem.Rect.Width / 2 - Viewer.Viewport.Width / 2) * Viewer.Zoom);
239 | var y = (int)(-1 * (elem.Rect.Y + elem.Rect.Height / 2 - Viewer.Viewport.Height / 2) * Viewer.Zoom);
240 | Viewer.SetZero(x, y);
241 | if (FocusElement == null)
242 | FocusElement = new Canvas.FocusElement(this);
243 |
244 | FocusElement.SetFocus(new Point(elem.Rect.X + elem.Rect.Width / 2, elem.Rect.Y + elem.Rect.Height / 2));
245 | FocusElement.IsShow = true;
246 | }
247 |
248 |
249 | public void RemoveObjElement(string name, Action ElemCallback = null)
250 | {
251 | var layer = Layers.FirstOrDefault(x => x.Name == name);
252 | if (layer == null) return;
253 | if (ElementEditor.SelectedElements.Count == 0) return;
254 | ElementEditor.SelectedElements.ForEach(x =>
255 | {
256 | ElemCallback?.Invoke(x);
257 | });
258 | ElementEditor.RemoveSelectElements();
259 | }
260 |
261 | ///
262 | /// 获得单个对象
263 | ///
264 | public ObjElement GetElement(Point mousePoint)
265 | {
266 | var point = Viewer.MousePointToLocal(mousePoint);
267 | foreach (var item in Layers)
268 | {
269 | if (item.IsActive == false) continue;//TODO:此处可以进行优化,只对显示的对象进行检索提高效率
270 | var elm = item.Elements.FirstOrDefault(x => x.Rect.Contains(point) == true);
271 | if (elm != null)
272 | {
273 | return elm;
274 | }
275 | }
276 | return null;
277 | }
278 |
279 | ///
280 | /// 获得显示的单个对象
281 | ///
282 | public ObjElement GetVisibleElement(Point mousePoint)
283 | {
284 | var point = Viewer.MousePointToLocal(mousePoint);
285 | foreach (var item in Layers)
286 | {
287 | //没有显示的和非交互图层就不能被获得对象
288 | if (item.IsVisible == false || item.IsInteractionLayer == false) continue;//TODO:此处可以进行优化,只对显示的对象进行检索提高效率
289 | var elm = item.Elements.FirstOrDefault(x => x.Rect.Contains(point) == true);
290 | if (elm != null)
291 | {
292 | return elm;
293 | }
294 | }
295 | return null;
296 | }
297 | #endregion
298 |
299 | #region 图层操作
300 | ///
301 | /// 设置当前图层
302 | ///
303 | ///
304 | public void SetCurrentLayer(string name)
305 | {
306 | Layers.ForEach(x => x.IsActive = false);
307 | var layer = Layers.FirstOrDefault(x => x.Name == name);
308 | SetCurrentLayer(layer);
309 | }
310 |
311 | public void SetCurrentLayer(Layer layer)
312 | {
313 | if (layer != null)
314 | {
315 | layer.IsActive = true;//当前图层必须是激活的
316 | layer.IsVisible = true;//当前图层必须是可见的
317 | CurrentLayer = layer;
318 | LayerChanged?.Invoke(layer);
319 | }
320 | }
321 |
322 | ///
323 | /// 设置图层显示
324 | ///
325 | ///
326 | ///
327 | ///
328 | public bool SetLayerDisplay(string name)
329 | {
330 | var layer = Layers.FirstOrDefault(x => x.Name == name);
331 | return SetLayerDisplay(layer);
332 | }
333 | public bool SetLayerDisplay(Layer layer)
334 | {
335 | if (layer == null) return false;
336 | layer.IsVisible = true;
337 | this.Refresh();
338 | return true;
339 | }
340 |
341 | ///
342 | /// 设置图层隐藏
343 | ///
344 | ///
345 | ///
346 | ///
347 | public bool SetLayerHidden(string name)
348 | {
349 | var layer = Layers.FirstOrDefault(x => x.Name == name);
350 | return SetLayerHidden(layer);
351 | }
352 | public bool SetLayerHidden(Layer layer)
353 | {
354 | if (layer == null) return false;
355 | layer.IsVisible = false;
356 | this.Refresh();
357 | return true;
358 | }
359 |
360 | #endregion
361 |
362 | #region 工具提示支持
363 |
364 | private Point ToolTipMousePosition = new Point();
365 |
366 | ///
367 | /// 拖拽对象
368 | ///
369 | public IToolTip ToolTip;
370 |
371 | Timer ToolTipTimer;
372 |
373 | private void InitialToolTip()
374 | {
375 | ToolTipTimer = new Timer();
376 | ToolTipTimer.Interval = 400;
377 | ToolTipTimer.Tick += ToolTipTimer_Tick;
378 | ToolTipTimer.Start();
379 | }
380 |
381 | private void ToolTipTimer_Tick(object sender, EventArgs e)
382 | {
383 | if (IsRootFormActivated == false) return;
384 | if (ToolTip == null) return;
385 | if (ToolTipMousePosition == MousePosition)
386 | {
387 | //只有继承了提示接口的才能用于提示
388 | var objElement = GetVisibleElement(this.PointToClient(ToolTipMousePosition)) as IToolTipElement;
389 | if (objElement == null) return;
390 | ToolTip.Show(objElement);
391 | }
392 | else if (ToolTipMousePosition != MousePosition)
393 | {
394 | ToolTip.Hide();
395 | ToolTipMousePosition = MousePosition;
396 | }
397 | }
398 |
399 | #endregion
400 |
401 | protected override void Dispose(bool disposing)
402 | {
403 | base.Dispose(disposing);
404 |
405 | ElementEditor?.SelectedElements?.Clear();
406 |
407 | Layers?.ForEach(x =>
408 | {
409 | x.Elements.Clear();
410 | });
411 |
412 | Layers?.Clear();
413 |
414 | ToolTipTimer?.Stop();
415 | ToolTipTimer?.Dispose();
416 | }
417 |
418 | }
419 |
420 | }
421 |
--------------------------------------------------------------------------------
/CanvasDemo/CanvasDemo/Canvas/ElementEditor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 |
9 | namespace CanvasDemo.Canvas
10 | {
11 | ///
12 | /// 选择框对象
13 | ///
14 | public class ElementEditor : Element
15 | {
16 | SelectionBox SelectionBox;
17 |
18 | public ElementEditor(TimCanvas canvas) : base(canvas, nameof(ElementEditor))
19 | {
20 | SelectionBox = new SelectionBox(this, canvas);
21 |
22 | }
23 |
24 | public override void Drawing(Graphics g)
25 | {
26 | //绘制选择对象的拖动柄
27 | SelectedElements.ForEach(x => x.DrawingJoystick(g));
28 |
29 | SelectionBox.Drawing(g);
30 | }
31 |
32 | public Point MPoint;
33 | //对象状态
34 | private EditorState EState = EditorState.None;
35 | //拖动柄状态
36 | private TransformState TState = TransformState.None;
37 |
38 | public void MouseDown(MouseEventArgs e)
39 | {
40 | if (e.Button == MouseButtons.Left)
41 | {
42 | MPoint = Viewer.MousePointToLocal(e.Location);
43 | var elem = SelectedElements.FirstOrDefault(x => x.Rect.Contains(MPoint) == true);
44 | if (elem != null)
45 | {//点击已经选择的对象
46 | SetCurrent(elem);//设定当前点的对象为操作对象
47 |
48 | if (Canvas.IsLocked == false)
49 | {//如果是只读,那么就不要进入移动和调整大小模式
50 | if (MPoint.X > elem.Rect.Right - elem.JoystickSize && MPoint.Y > elem.Rect.Bottom - elem.JoystickSize && MPoint.X < elem.Rect.Right && MPoint.Y < elem.Rect.Bottom)
51 | {
52 | EState = EditorState.Transform;
53 | TState = TransformState.RightBottom;
54 | }
55 | else if (MPoint.X > elem.Rect.Right - elem.JoystickSize && MPoint.Y > elem.Rect.Y + elem.Rect.Height / 2 - elem.JoystickSize / 2 && MPoint.X < elem.Rect.Right && MPoint.Y < elem.Rect.Y + elem.Rect.Height / 2 + elem.JoystickSize / 2)
56 | {
57 | EState = EditorState.Transform;
58 | TState = TransformState.Right;
59 | }
60 | else if (MPoint.X > elem.Rect.X + elem.Rect.Width / 2 - elem.JoystickSize / 2 && MPoint.Y > elem.Rect.Bottom - elem.JoystickSize && MPoint.X < elem.Rect.X + elem.Rect.Width / 2 + elem.JoystickSize / 2 && MPoint.Y < elem.Rect.Bottom)
61 | {
62 | EState = EditorState.Transform;
63 | TState = TransformState.Bottom;
64 | }
65 | else
66 | {
67 | EState = EditorState.Move;
68 | }
69 | }
70 |
71 | }
72 | else
73 | {
74 | EState = EditorState.Selection;
75 | SelectionBox.MouseDown(e);
76 | }
77 | }
78 | }
79 |
80 | public void MouseMove(MouseEventArgs e)
81 | {
82 | if (EState == EditorState.Move)
83 | {//移动模式,设定对象位置
84 | var end = Viewer.MousePointToLocal(e.Location);
85 | var x = (end.X - MPoint.X);
86 | var y = (end.Y - MPoint.Y);
87 |
88 | SelectedElements.ForEach(elem => elem.Rect.Offset(x, y));
89 | MPoint = end;
90 | }
91 | else if (EState == EditorState.Transform)
92 | {//调整大小
93 | var end = Viewer.MousePointToLocal(e.Location);
94 | var x = (end.X - MPoint.X);
95 | var y = (end.Y - MPoint.Y);
96 |
97 | SelectedElements.ForEach(elem =>
98 | {
99 | switch (TState)
100 | {
101 | case TransformState.RightBottom:
102 | elem.Rect.Width += x;
103 | elem.Rect.Height += y;
104 | break;
105 | case TransformState.Right:
106 | elem.Rect.Width += x;
107 | break;
108 | case TransformState.Bottom:
109 | elem.Rect.Height += y;
110 | break;
111 | }
112 |
113 | if (elem.Rect.Width < 10) elem.Rect.Width = 10;
114 | if (elem.Rect.Height < 10) elem.Rect.Height = 10;
115 | });
116 |
117 | MPoint = end;
118 | }
119 | else if (EState == EditorState.Selection)
120 | {//选择
121 | SelectionBox.MouseMove(e);
122 |
123 | }
124 | }
125 |
126 | public void MouseUp(MouseEventArgs e)
127 | {
128 | if (EState == EditorState.Selection)
129 | {
130 | SelectionBox.MouseUp(e);
131 | }
132 |
133 | EState = EditorState.None;
134 | }
135 |
136 | public void MouseWheel(MouseEventArgs e)
137 | {
138 |
139 | }
140 |
141 | #region 对象选择操作
142 |
143 | ///
144 | /// 选择的对象集合
145 | ///
146 | public List SelectedElements = new List();
147 |
148 | ///
149 | /// 当前对象
150 | ///
151 | public ObjElement CurrentElement = null;
152 |
153 | ///
154 | /// 清除选的的对象
155 | ///
156 | public void ClearSelected()
157 | {
158 | SelectedElements.ForEach(x => x?.UnSelected());
159 | SelectedElements.Clear();
160 | }
161 |
162 | ///
163 | /// 设置当前选择的对象
164 | ///
165 | ///
166 | public void SetCurrent(ObjElement element)
167 | {
168 | CurrentElement?.UnCurrent();
169 | CurrentElement = element;
170 | CurrentElement?.Current();
171 | }
172 |
173 | ///
174 | /// 添加选中的对象
175 | ///
176 | ///
177 | public void AddSelected(List elements)
178 | {
179 | SelectedElements.AddRange(elements);
180 | elements.ForEach(x => x?.Selected());
181 |
182 | SelectedObjElementsEvent?.Invoke(SelectedElements);
183 |
184 | }
185 | ///
186 | /// 选择了对象元素后触发此方法
187 | ///
188 | public event Action> SelectedObjElementsEvent;
189 |
190 |
191 |
192 | #endregion
193 |
194 |
195 |
196 | #region 元素操作
197 |
198 | ///
199 | /// 被删除的对象,可以理解成当前布局的回收站,可以从这里拿回已经删除的对象
200 | ///
201 | public Dictionary DeletedElems = new Dictionary();
202 |
203 | ///
204 | /// 移除选择的对象
205 | ///
206 | public void RemoveSelectElements()
207 | {
208 | foreach (var item in SelectedElements)
209 | {
210 | item.Layer.Elements.Remove(item);
211 | DeletedElems.Add(item.ID, item);
212 | }
213 | ClearSelected();
214 |
215 | SelectedObjElementsEvent?.Invoke(SelectedElements);
216 |
217 | }
218 |
219 | #endregion
220 |
221 | #region 对象布局操作
222 |
223 | ///
224 | /// 左对齐
225 | ///
226 | public void AlignLeft()
227 | {
228 | if (SelectedElements.Count <= 1) return;
229 |
230 | foreach (var item in SelectedElements)
231 | {
232 | item.Rect.X = CurrentElement.Rect.X;
233 | }
234 | Canvas.Refresh();
235 | }
236 |
237 | ///
238 | /// 右对齐
239 | ///
240 | public void AlignRight()
241 | {
242 | if (SelectedElements.Count <= 1) return;
243 |
244 | foreach (var item in SelectedElements)
245 | {
246 | item.Rect.X = CurrentElement.Rect.Right - item.Rect.Width;
247 | };
248 | Canvas.Refresh();
249 | }
250 |
251 | ///
252 | /// 上对齐
253 | ///
254 | public void AlignTop()
255 | {
256 | if (SelectedElements.Count <= 1) return;
257 |
258 | foreach (var item in SelectedElements)
259 | {
260 | item.Rect.Y = CurrentElement.Rect.Y;
261 | };
262 | Canvas.Refresh();
263 | }
264 |
265 | ///
266 | /// 下对齐
267 | ///
268 | public void AlignBottom()
269 | {
270 | if (SelectedElements.Count <= 1) return;
271 |
272 | foreach (var item in SelectedElements)
273 | {
274 | item.Rect.Y = CurrentElement.Rect.Bottom - item.Rect.Height;
275 | };
276 | Canvas.Refresh();
277 | }
278 |
279 | ///
280 | /// 居中齐
281 | ///
282 | public void AlignCenter()
283 | {
284 | if (SelectedElements.Count <= 1) return;
285 |
286 | var center = CurrentElement.Rect.X + CurrentElement.Rect.Width / 2;
287 |
288 | foreach (var item in SelectedElements)
289 | {
290 | item.Rect.X = center - item.Rect.Width / 2;
291 | };
292 | Canvas.Refresh();
293 | }
294 |
295 | ///
296 | /// 中间齐
297 | ///
298 | public void AlignMiddle()
299 | {
300 | if (SelectedElements.Count <= 1) return;
301 |
302 | var middle = CurrentElement.Rect.Y + CurrentElement.Rect.Height / 2;
303 |
304 | foreach (var item in SelectedElements)
305 | {
306 | item.Rect.Y = middle - item.Rect.Height / 2;
307 | };
308 | Canvas.Refresh();
309 | }
310 |
311 | ///
312 | /// 宽度相同
313 | ///
314 | public void SameWidth()
315 | {
316 | if (SelectedElements.Count <= 1) return;
317 |
318 | foreach (var item in SelectedElements)
319 | {
320 | item.Rect.Width = CurrentElement.Rect.Width;
321 | };
322 | Canvas.Refresh();
323 | }
324 |
325 | ///
326 | /// 高度相同
327 | ///
328 | public void SameHeight()
329 | {
330 | if (SelectedElements.Count <= 1) return;
331 |
332 | foreach (var item in SelectedElements)
333 | {
334 | item.Rect.Height = CurrentElement.Rect.Height;
335 | };
336 | Canvas.Refresh();
337 | }
338 |
339 | ///
340 | /// 大小相同
341 | ///
342 | public void SameSize()
343 | {
344 | if (SelectedElements.Count <= 1) return;
345 |
346 | foreach (var item in SelectedElements)
347 | {
348 | item.Rect.Width = CurrentElement.Rect.Width;
349 | item.Rect.Height = CurrentElement.Rect.Height;
350 | };
351 | Canvas.Refresh();
352 | }
353 |
354 | ///
355 | /// 水平间距相同
356 | ///
357 | public void SameHorizontalSpace()
358 | {
359 | if (SelectedElements.Count <= 1) return;
360 |
361 | var orderFans = SelectedElements.OrderBy(x => x.Rect.X).ToList();
362 | var minLeft = orderFans.First().Rect.X;
363 | var maxLeft = orderFans.Last().Rect.X;
364 | var distance = maxLeft - minLeft;
365 |
366 | for (int i = 0; i < orderFans.Count; i++)
367 | {
368 | orderFans[i].Rect.X = (int)(minLeft + (float)i / ((float)orderFans.Count - 1.0f) * distance);
369 | }
370 | Canvas.Refresh();
371 | }
372 |
373 | ///
374 | /// 垂直间距相同
375 | ///
376 | public void SameVerticalSpace()
377 | {
378 | if (SelectedElements.Count <= 1) return;
379 |
380 | var orderFans = SelectedElements.OrderBy(x => x.Rect.Y).ToList();
381 | var minTop = orderFans.First().Rect.Y;
382 | var maxTop = orderFans.Last().Rect.Y;
383 | var distance = maxTop - minTop;
384 |
385 | for (int i = 0; i < orderFans.Count; i++)
386 | {
387 | orderFans[i].Rect.Y = (int)(minTop + (float)i / ((float)orderFans.Count - 1.0f) * distance);
388 | }
389 | Canvas.Refresh();
390 | }
391 |
392 | public override void DrawingAfter(Graphics g)
393 | {
394 |
395 | }
396 |
397 |
398 | #endregion
399 |
400 |
401 | enum EditorState
402 | {
403 | None,//没有任何操作
404 | Selection,//框选状态
405 | Move,//移动状态
406 | Transform,//调整大小状态
407 | }
408 |
409 |
410 | enum TransformState
411 | {
412 | None,
413 | RightBottom,
414 | Right,
415 | Bottom,
416 |
417 | }
418 | }
419 |
420 |
421 |
422 | }
--------------------------------------------------------------------------------