tangxu
2024-05-08 02a032030f8fe8acfece20e1ddb37cdc9426e808
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
50
51
52
53
54
55
56
57
58
59
60
61
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
 
namespace CloudWaterNetwork
{
    partial class MapControl : Control
    {
        private Point backgroundImageOffset = Point.Empty;  // 背景图偏移量
        private Image backgroundImage;                      // 背景图片
        private RectangleF chartArea;                        // 直角坐标系范围
        private PointF origin;                               // 坐标轴原点
        private float zoomLevel = 1f;                        // 缩放级别
 
        private bool isPanning = false;                      // 是否正在平移
        private Point lastMousePosition = Point.Empty;       // 上一次鼠标位置
        private bool isSelecting = false;                    // 是否正在框选矩形
        private RectangleF selectionRect;                    // 框选矩形
 
 
        public MapControl()
        {
            this.DoubleBuffered = true;
        }
 
        public Image BackgroundImage
        {
            get { return this.backgroundImage; }
            set
            {
                this.backgroundImage = value;
                this.Invalidate();
            }
        }
 
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
 
            if (e.Button == MouseButtons.Middle)
            {
                // 启动平移模式
                this.isPanning = true;
                this.lastMousePosition = e.Location;
            }
            else if (e.Button == MouseButtons.Left)
            {
                // 启动框选矩形模式
                this.isSelecting = true;
                this.selectionRect = new RectangleF(e.Location, SizeF.Empty);
            }
        }
 
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
 
            if (this.isPanning)
            {
                // 平移坐标系范围和背景图片
                int dx = e.X - this.lastMousePosition.X;
                int dy = e.Y - this.lastMousePosition.Y;
                this.backgroundImageOffset.Offset(dx, dy);
                this.chartArea.Offset(dx / this.zoomLevel, -dy / this.zoomLevel);
                this.origin.X += dx;
                this.origin.Y += dy;
                //Offset(dx, dy);
                this.lastMousePosition = e.Location;
                this.Invalidate();
            }
            else if (this.isSelecting)
            {
                // 更新框选矩形范围
                this.selectionRect.Width = e.X - this.selectionRect.Left;
                this.selectionRect.Height = e.Y - this.selectionRect.Top;
                this.Invalidate();
            }
        }
 
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
 
            if (this.isPanning)
            {
                // 停止平移模式
                this.isPanning = false;
            }
            else if (this.isSelecting)
            {
                // 停止框选矩形模式,并重新计算坐标系范围和原点位置
                this.isSelecting = false;
 
                float x1 = (this.selectionRect.Left - this.origin.X) * (this.chartArea.Width / this.ClientSize.Width) + this.chartArea.Left;
                float y1 = (this.ClientSize.Height - this.selectionRect.Bottom + this.origin.Y) * (this.chartArea.Height / this.ClientSize.Height) + this.chartArea.Top;
                float x2 = (this.selectionRect.Right - this.origin.X) * (this.chartArea.Width / this.ClientSize.Width) + this.chartArea.Left;
                float y2 = (this.ClientSize.Height - this.selectionRect.Top + this.origin.Y) * (this.chartArea.Height / this.ClientSize.Height) + this.chartArea.Top;
 
                this.chartArea = new RectangleF(x1, y1, x2 - x1, y2 - y1);
                this.origin = new PointF(this.chartArea.Left, this.chartArea.Bottom);
 
                this.Invalidate();
            }
        }
 
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
 
            // 缩放坐标系范围和背景图片
            float factor = e.Delta > 0 ? 1.1f : 0.9f;
            float oldZoomLevel = this.zoomLevel;
            this.zoomLevel *= factor;
            if (this.zoomLevel < 0.1f)
            {
                this.zoomLevel = 0.1f;
            }
            if (this.zoomLevel > 10f)
            {
                this.zoomLevel = 10f;
            }
            float dx = (this.chartArea.Width / oldZoomLevel - this.chartArea.Width / this.zoomLevel) / 2;
            float dy = (this.chartArea.Height / oldZoomLevel - this.chartArea.Height / this.zoomLevel) / 2;
            this.backgroundImageOffset.Offset((int)(e.Location.X * dx), (int)(e.Location.Y * dy));
            this.chartArea.Inflate(dx, dy);
            this.origin = new PointF(this.chartArea.Left, this.chartArea.Bottom);
 
            this.Invalidate();
        }
 
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
 
            // 绘制背景图片
            if (this.backgroundImage != null)
            {
                e.Graphics.DrawImage(this.backgroundImage, this.backgroundImageOffset);
            }
 
            // 绘制直角坐标系和矩形
            using (Pen axisPen = new Pen(Color.Black, 2))
            {
                e.Graphics.DrawLine(axisPen, this.origin.X, this.origin.Y, this.origin.X + this.chartArea.Width / this.zoomLevel, this.origin.Y);  // X轴
                e.Graphics.DrawLine(axisPen, this.origin.X, this.origin.Y, this.origin.X, this.origin.Y - this.chartArea.Height / this.zoomLevel);  // Y轴
 
                if (this.isSelecting)
                {
                    // 绘制框选矩形
                    using (Pen selectionPen = new Pen(Color.Red, 2))
                    {
                        selectionPen.DashStyle = DashStyle.Dot;
                        e.Graphics.DrawRectangle(selectionPen, Rectangle.Round(this.selectionRect));
                    }
                }
            }
        }
 
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
 
            // 初始化坐标系范围和原点位置
            float widthRatio = (float)this.ClientSize.Width / this.chartArea.Width;
            float heightRatio = (float)this.ClientSize.Height / this.chartArea.Height;
            float zoomRatio = Math.Min(widthRatio, heightRatio);
            this.chartArea = new RectangleF(0, 0, this.ClientSize.Width / zoomRatio, this.ClientSize.Height / zoomRatio);
            this.origin = new PointF(this.chartArea.Left, this.chartArea.Bottom);
 
            this.Invalidate();
        }
    }
}