5 Star 10 Fork 3

刘小勇/SCADA

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
CanvasContainer.cs 16.66 KB
一键复制 编辑 原始数据 按行查看 历史
using SCADAEditor.Component;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace SCADAEditor.Container
{
/// <summary>
/// 画布容器类,用于承载和管理所有组态组件
/// </summary>
public class CanvasContainer : Control
{
private Size _contentSize = new Size(800, 600); // 内容区域大小
private HScrollBar hScrollBar = new HScrollBar();
private VScrollBar vScrollBar = new VScrollBar();
private Point _viewOffset = Point.Empty; // 视图偏移量
private enum ResizeHandle
{
None,
TopLeft, Top, TopRight,
Left, Right,
BottomLeft, Bottom, BottomRight
}
// 当前选中的组件对象
private ComponentBase _selectedComponent;
// 当前操作的调整控制点类型(无/左上/上/右上/左/右/左下/下/右下)
private ResizeHandle _currentHandle = ResizeHandle.None;
// 鼠标拖动起始位置坐标
private Point _dragStartPosition;
// 组件原始边界矩形(用于大小调整时计算增量)
private Rectangle _originalBounds;
/// <summary>
/// 当前画布上的所有组件集合
/// </summary>
public List<ComponentBase> Components { get; } = new List<ComponentBase>();
// 新增框选相关字段
private Point _selectionStart;
private Rectangle _selectionRect;
private bool _isSelecting = false;
public CanvasContainer()
{
this.AllowDrop = true; // 必须设置此项
// 启用双缓冲
this.DoubleBuffered = true;
// 初始化滚动条
hScrollBar.Dock = DockStyle.Bottom;
vScrollBar.Dock = DockStyle.Right;
hScrollBar.Scroll += (s, e) => {
_viewOffset.X = e.NewValue;
Invalidate();
};
vScrollBar.Scroll += (s, e) => {
_viewOffset.Y = e.NewValue;
Invalidate();
};
this.Controls.Add(hScrollBar);
this.Controls.Add(vScrollBar);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
UpdateScrollBars();
}
private void UpdateScrollBars()
{
// 更新水平滚动条
hScrollBar.Enabled = _contentSize.Width > this.ClientSize.Width;
hScrollBar.Maximum = _contentSize.Width;
hScrollBar.LargeChange = this.ClientSize.Width;
// 更新垂直滚动条
vScrollBar.Enabled = _contentSize.Height > this.ClientSize.Height;
vScrollBar.Maximum = _contentSize.Height;
vScrollBar.LargeChange = this.ClientSize.Height;
}
public Size ContentSize
{
get { return _contentSize; }
set
{
_contentSize = value;
UpdateScrollBars();
Invalidate();
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e); // 调用基类鼠标按下事件处理
// // 清除所有组件的选中状态
// foreach (var component in Components)
// {
// component.IsSelected = false;
// }
// 如果不是点击组件或控制点,开始框选
if (!Components.Any(c => c.IsEditMode && c.HitTest(e.Location)))
{
_isSelecting = true;
_selectionStart = e.Location;
_selectionRect = new Rectangle(e.Location, Size.Empty);
}
// 检查是否点击了空白区域
bool clickedOnComponent = Components.Any(c => c.IsEditMode && c.HitTest(e.Location));
var clickedComponent = Components.LastOrDefault(c => c.IsEditMode && c.HitTest(e.Location));
// 如果点击的组件未被选中,清除所有选中状态
if (clickedComponent != null && !clickedComponent.IsSelected)
{
foreach (var component in Components)
{
component.IsSelected = false;
}
}
// // 清除所有组件的选中状态
// foreach (var component in Components)
// {
// component.IsSelected = false;
// }
// 如果是空白区域点击,直接返回并重绘
if (!clickedOnComponent)
{
foreach (var component in Components)
{
component.IsSelected = false;
}
_selectedComponent = null;
Invalidate();
return;
}
// 先检查是否点击了任何组件的控制点
foreach (var component in Components.Where(c => c.IsEditMode))
{
_selectedComponent = component;
_currentHandle = GetResizeHandleAtPoint(e.Location);
if (_currentHandle != ResizeHandle.None)
{
_dragStartPosition = e.Location;
_originalBounds = new Rectangle(component.Position, component.Size);
Invalidate(); // 立即重绘显示控制点
return;
}
}
// 如果没有点击控制点,则选择组件
_selectedComponent = Components.LastOrDefault(c =>
c.IsEditMode && c.HitTest(e.Location));
// 如果选中了组件,准备拖动
if (_selectedComponent != null)
{
_selectedComponent.IsSelected = true;
_dragStartPosition = e.Location;
_originalBounds = new Rectangle(_selectedComponent.Position, _selectedComponent.Size);
Invalidate(); // 立即重绘显示控制点
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e); // 调用基类鼠标移动事件处理
if (_isSelecting && e.Button == MouseButtons.Left)
{
// 更新选择矩形
int x = Math.Min(_selectionStart.X, e.X);
int y = Math.Min(_selectionStart.Y, e.Y);
int width = Math.Abs(e.X - _selectionStart.X);
int height = Math.Abs(e.Y - _selectionStart.Y);
_selectionRect = new Rectangle(x, y, width, height);
Invalidate();
}
// 如果不是左键按下则直接返回
if (e.Button != MouseButtons.Left) return;
if (_selectedComponent != null && _selectedComponent.IsEditMode)
{
if (_currentHandle != ResizeHandle.None)
{
// 调整大小逻辑
if (_selectedComponent != null && _selectedComponent.IsEditMode && _currentHandle != ResizeHandle.None)
{
// 计算鼠标移动的增量(相对于拖动起始位置)
var deltaX = e.X - _dragStartPosition.X;
var deltaY = e.Y - _dragStartPosition.Y;
// 应用平滑处理:只有当移动距离超过1像素时才执行调整
if (Math.Abs(deltaX) > 1 || Math.Abs(deltaY) > 1)
{
// 调用调整组件大小的方法
ResizeComponent(new Point(deltaX, deltaY));
// 触发重绘
Invalidate();
}
}
}
else
{
// // 拖动组件逻辑
// var deltaX = e.X - _dragStartPosition.X;
// var deltaY = e.Y - _dragStartPosition.Y;
// if (Math.Abs(deltaX) > 1 || Math.Abs(deltaY) > 1)
// {
// _selectedComponent.Position = new Point(
// _originalBounds.X + deltaX,
// _originalBounds.Y + deltaY);
// Invalidate();
// }
// 拖动组件逻辑
var deltaX = e.X - _dragStartPosition.X;
var deltaY = e.Y - _dragStartPosition.Y;
if (Math.Abs(deltaX) > 1 || Math.Abs(deltaY) > 1)
{
// 移动所有选中的组件
foreach (var component in Components.Where(c => c.IsSelected))
{
component.Position = new Point(
component.Position.X + deltaX,
component.Position.Y + deltaY);
}
// 更新拖动起始位置
_dragStartPosition = e.Location;
Invalidate();
}
}
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (_isSelecting)
{
// 选中矩形区域内的所有组件
foreach (var component in Components.Where(c => c.IsEditMode))
{
if (_selectionRect.IntersectsWith(new Rectangle(component.Position, component.Size)))
{
component.IsSelected = true;
}
}
_isSelecting = false;
_selectionRect = Rectangle.Empty;
Invalidate();
}
_currentHandle = ResizeHandle.None;
base.OnMouseUp(e);
}
/// <summary>
/// 根据鼠标位置获取当前点击的控制点类型
/// </summary>
/// <param name="point">鼠标位置</param>
/// <returns>当前点击的控制点类型</returns>
private ResizeHandle GetResizeHandleAtPoint(Point point)
{
if (_selectedComponent == null) return ResizeHandle.None; // 如果没有选中组件则返回None
var rect = new Rectangle(_selectedComponent.Position, _selectedComponent.Size);
const int handleSize = 8; // 控制点大小设置为8像素
// 检查8个控制点是否包含当前鼠标位置
if (new Rectangle(rect.Left - handleSize/2, rect.Top - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.TopLeft; // 左上角控制点
if (new Rectangle(rect.Right - handleSize/2, rect.Top - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.TopRight; // 右上角控制点
if (new Rectangle(rect.Left - handleSize/2, rect.Bottom - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.BottomLeft; // 左下角控制点
if (new Rectangle(rect.Right - handleSize/2, rect.Bottom - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.BottomRight; // 右下角控制点
if (new Rectangle(rect.Left + rect.Width/2 - handleSize/2, rect.Top - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.Top; // 上边中点控制点
if (new Rectangle(rect.Left + rect.Width/2 - handleSize/2, rect.Bottom - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.Bottom; // 下边中点控制点
if (new Rectangle(rect.Left - handleSize/2, rect.Top + rect.Height/2 - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.Left; // 左边中点控制点
if (new Rectangle(rect.Right - handleSize/2, rect.Top + rect.Height/2 - handleSize/2, handleSize, handleSize).Contains(point))
return ResizeHandle.Right; // 右边中点控制点
return ResizeHandle.None; // 没有点击任何控制点
}
private void ResizeComponent(Point delta)
{
// 添加最小移动阈值
if (Math.Abs(delta.X) < 1 && Math.Abs(delta.Y) < 1) return;
var newBounds = _originalBounds;
switch (_currentHandle)
{
case ResizeHandle.TopLeft:
newBounds.X += delta.X;
newBounds.Y += delta.Y;
newBounds.Width -= delta.X;
newBounds.Height -= delta.Y;
break;
case ResizeHandle.Top:
newBounds.Y += delta.Y;
newBounds.Height -= delta.Y;
break;
case ResizeHandle.TopRight:
newBounds.Y += delta.Y;
newBounds.Width += delta.X;
newBounds.Height -= delta.Y;
break;
case ResizeHandle.Left:
newBounds.X += delta.X;
newBounds.Width -= delta.X;
break;
case ResizeHandle.Right:
newBounds.Width += delta.X;
break;
case ResizeHandle.BottomLeft:
newBounds.X += delta.X;
newBounds.Width -= delta.X;
newBounds.Height += delta.Y;
break;
case ResizeHandle.Bottom:
newBounds.Height += delta.Y;
break;
case ResizeHandle.BottomRight:
newBounds.Width += delta.X;
newBounds.Height += delta.Y;
break;
}
// 确保最小尺寸
if (newBounds.Width < 10) newBounds.Width = 10;
if (newBounds.Height < 10) newBounds.Height = 10;
_selectedComponent.Position = newBounds.Location;
_selectedComponent.Size = newBounds.Size;
}
/// <summary>
/// 向画布添加新组件
/// </summary>
/// <param name="component">要添加的组件对象</param>
public void AddComponent(ComponentBase component)
{
Components.Add(component);
Invalidate(); // 触发重绘
}
/// <summary>
/// 重写绘制方法,渲染所有组件
/// </summary>
/// <param name="e">绘制事件参数</param>
protected override void OnPaint(PaintEventArgs e)
{
//base.OnPaint(e);
// 使用双缓冲后,不需要调用base.OnPaint(e)
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// 应用视图偏移
e.Graphics.TranslateTransform(-_viewOffset.X, -_viewOffset.Y);
// 绘制内容
foreach (var component in Components)
{
component.Draw(e.Graphics);
}
// 绘制容器边界
using (var pen = new Pen(Color.Gray, 1) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dash })
{
e.Graphics.DrawRectangle(pen, new Rectangle(Point.Empty, _contentSize));
}
// 恢复变换
e.Graphics.ResetTransform();
// 绘制选择矩形(虚线框)
if (_isSelecting && !_selectionRect.IsEmpty)
{
using (var pen = new Pen(Color.Blue, 1) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dash })
{
e.Graphics.DrawRectangle(pen, _selectionRect);
}
}
// 添加边框绘制
using (var borderPen = new Pen(Color.Black, 1))
{
e.Graphics.DrawRectangle(borderPen, new Rectangle(0, 0, Width - 1, Height - 1));
}
}
/// <summary>
/// 将当前画布状态保存为JSON字符串
/// </summary>
/// <returns>包含所有组件状态的JSON字符串</returns>
public string SaveToJson()
{
// 序列化逻辑
return "";
}
/// <summary>
/// 从JSON字符串加载画布状态
/// </summary>
/// <param name="json">包含组件状态的JSON字符串</param>
public void LoadFromJson(string json)
{
// 反序列化逻辑
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/netMarketing/SCADA.git
git@gitee.com:netMarketing/SCADA.git
netMarketing
SCADA
SCADA
master

搜索帮助