图形化控件开发 - 矩形控件

最近这段时间,公司需要做一个图形化配置工具。

主要是有以下一些要求:

1、针对一些底层的智能设备(智能楼宇的控制器),通过COM口连接上,并通过相关协议读取参数值

2、支持一些基本的逻辑运算,以及公司业务的一些特性“点”,一种点可以看成一个图形化控件

3、参数在图形化工具上显示出来,工程人员直接设置图形属性设置参数值、连线等

4、支持在线和离线2种工作方式,设置好参数后直接在界面上下载到设备里

5、工作太支持拖动,连线,放大缩小,保存设计文件等

6、“点”的本身业务要求,略。。。

 

经过一番调研,winfrom的图形化控件技术,并没有成熟的框架出来。

只有一些零碎的demo,大体上的思路是通过重绘来实现具体的图形控件。

在这里也提一下Netron框架,这个框架代码我看了很多遍,很多思路都从这里来,但是太繁琐了,改动起来非常吃力

所以我自己重新写了一遍,大体思路没变,只是走精简路线,以下是我们的工具最终的界面样式,

我打算把每一步都分拆贴出来,做成一个教程。给大家一个参考

 

 

1、新建自定义控件抽象类

1.1 包含一些基本属性,如字体、是否悬停、是否选中、画布

1.2 包含几个抽象方法,绘制控件皮肤、移动、悬停判断、重新绘制

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
public abstract class AbstractShape
{
    [Browsable(false)]
    public Font DefaultFont { get; private set; }
 
    [Browsable(false)]
    public Pen DefaultPen { get; private set; }
 
    [Browsable(false)]
    public bool IsHover { get; set; }
 
    [Browsable(false)]
    public bool IsSelected { get; set; }
 
    [Browsable(false)]
    public GraphControl Canvas { get; set; }
 
    public AbstractShape() { }
    public AbstractShape(GraphControl site)
    {
        DefaultFont = new Font("宋体", 10F);
        DefaultPen = new Pen(Brushes.Black, 1F);
        Canvas = site;
        IsHover = false;
        IsSelected = false;
    }
 
    public abstract void Paint(Graphics g);
    public abstract bool Hover(Point p);
    public abstract void Invalidate();
    public abstract void Move(Point p);
}

2、绘制一个自定义控件基类

包含一些基本属性及一些共性方法实现

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
public class ShapeBase : AbstractShape
{
    protected Rectangle RectangleBase;
 
    [Browsable(false)]
    public Brush ShapeBrush { get; protected set; }
 
    [Browsable(true), Description("Width"), Category("Layout")]
    public int Width
    {
        get { return this.RectangleBase.Width; }
        set { Resize(value, this.Height); }
    }
 
    [Browsable(true), Description("Height"), Category("Layout")]
    public int Height
    {
        get { return this.RectangleBase.Height; }
        set { Resize(this.Width, value); }
    }
 
    [Browsable(true), Description("X"), Category("Layout")]
    public int X
    {
        get { return RectangleBase.X; }
        set
        {
            Point p = new Point(value - RectangleBase.X, RectangleBase.Y);
            this.Move(p);
            Canvas.Invalidate();
        }
    }
 
    [Browsable(true), Description("Y"), Category("Layout")]
    public int Y
    {
        get { return RectangleBase.Y; }
        set
        {
            Point p = new Point(RectangleBase.X, value - RectangleBase.Y);
            this.Move(p);
            Canvas.Invalidate();
        }
    }
 
    [Browsable(true), Description("Text"), Category("Layout")]
    public string Text { getset; }
 
    public ShapeBase(GraphControl site)
        : base(site)
    {
        Init();
    }
 
    private void Init()
    {
        RectangleBase = new Rectangle(0, 0, 100, 70);
        ShapeBrush = new SolidBrush(Color.SteelBlue);
    }
 
    public override void Paint(Graphics g)
    {
        return;
    }
 
    public override bool Hover(Point p)
    {
        return false;
    }
 
    public override void Invalidate()
    {
        Canvas.Invalidate(RectangleBase);
    }
 
    public override void Move(Point p)
    {
        this.RectangleBase.X += p.X;
        this.RectangleBase.Y += p.Y;
    }
 
    public virtual void Resize(int width, int height)
    {
        this.RectangleBase.Height = height;
        this.RectangleBase.Width = width;
    }
}

3、新建一个简单的矩形类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LjrRectangle : ShapeBase
{
    public LjrRectangle(GraphControl s)
        : base(s)
    { }
 
    public override void Paint(Graphics g)
    {
        g.FillRectangle(base.ShapeBrush, base.RectangleBase);
        if (base.IsHover || base.IsSelected)
        {
            g.DrawRectangle(new Pen(Color.Red, 2F), base.RectangleBase);
        }
        else
        {
            g.DrawRectangle(base.DefaultPen, base.RectangleBase);
        }
    }
} 

4、新建画布GraphControl

所以自定义图形控件都将在这个画布里绘制、移动等操作

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
public class GraphControl : ScrollableControl
{
    /// <summary>属性控件绑定事件</summary>
    public delegate void ShowProperty(object ent);
 
    /// <summary>属性控件绑定事件</summary>
    public event ShowProperty OnShowProperty;
 
    /// <summary>当前悬停在哪个对象上边</summary>
    protected AbstractShape hoveredObject;
 
    /// <summary>当前选中的对象</summary>
    protected AbstractShape selectedObject;
 
    /// <summary>是否正在拖动</summary>
    protected bool draging = false;
 
    /// <summary>图形对象集合</summary>
    public List<ShapeBase> Shapes { get; set; }
 
    public GraphControl()
    {
        Shapes = new List<ShapeBase>();
    }
 
    protected override void OnPaintBackground(PaintEventArgs e)
    {
        base.OnPaintBackground(e);
 
        Graphics g = e.Graphics;
        Size gridSize = new Size(10, 10);
        ControlPaint.DrawGrid(g, this.ClientRectangle, gridSize, this.BackColor);
    }
 
    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
 
        for (int k = 0; k < Shapes.Count; k++)
        {
            Shapes[k].Paint(g);
        }
    }
 
    public ShapeBase AddShape(ShapeBase shape)
    {
        Shapes.Add(shape);
        shape.Canvas = this;
        this.Invalidate();
        return shape;
    }
}

5、新建测试项目

5.1 新建demo.cs把控件拖入界面左边

5.2 再拖一个PropertyGrid用来显示属性值

5.3 Demo.cs代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
private void Demo_Load(object sender, EventArgs e)
{
    var shape = new LjrRectangle(graphControl1);
    shape.Location = new Point(100, 300);
    shape.Text = "图形化控件";
    this.graphControl1.AddShape(shape);
    this.graphControl1.OnShowProperty += graphControl1_OnShowProps;
}
 
private void graphControl1_OnShowProps(object ent)
{
    this.propertyGrid1.SelectedObject = ent;
}

6、运行程序

 

 

阅读目录

  • 图形化控件开发 - 矩形控件
  • 图形化控件开发 - 控件移动
  • 图形化控件开发 - 直线连线
  • 图形化控件开发 - 折线连线
  • 图形化控件开发 - 放大缩小
  • 图形化控件开发 - 待定
posted @   ljr忒修斯之船  阅读(2711)  评论(8编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示