net: [[C#]]

使用的新工具

ArrayList类

ArrayList类是C#编程语言和.NET Framework中的一个类,它提供了在动态数组中存储对象的能力。使用ArrayList类,开发者可以方便地添加、删除、查找和排序动态数组中的元素。

ArrayList类的特点包括:

  1. 元素类型不需要提前指定。在必要时,ArrayList会自动调整其类型以适应添加的元素类型。

  2. 可以存储任何类型的对象,包括简单类型(例如int、bool等)和复杂类型(例如自定义的类实例)。

  3. 与普通的数组相比,动态数组可以在运行时自动调整大小,并且不需要手动管理内存。

  4. 支持在数组中插入、删除、查找和排序元素。

  5. 提供了一些有用的属性和方法,例如Count属性来获取数组的长度,Add方法来添加新的元素,IndexOf方法来查找元素的位置等。

需要注意的是,由于ArrayList存储的是对象,因此在将对象添加到ArrayList中时,实际上是添加了一个指向该对象的引用。这意味着如果修改了一个对象,那么在ArrayList中保存的引用也会受到影响。另外,在使用ArrayList类时还需要小心其性能,因为在一些情况下,使用更专门的集合类(例如List)可能会更高效。

GraphicsPath类

GraphicsPath是.NET Framework中的一个类,可以用来构建、查询和绘制图形路径。可以将GraphicsPath看作是一个路径,它由一系列线段、曲线段和其他形状组成。在创建GraphicsPath对象后,可以添加多段路径,使用LineTo、CurveTo、AddPie、AddRectangle、AddEllipse等方法添加图形。GraphicsPath还提供了一些查询方法,例如获取路径的边界、是否包含某个点等。

GraphicsPath类在绘制复杂的形状时非常有用。例如,如果需要绘制一条平滑的曲线,可以创建一个GraphicsPath对象,添加多个点,然后使用DrawPath或FillPath方法绘制。

下面是一个示例代码,该示例绘制了一个由多个线段和圆组成的图形路径:

1
2
3
4
5
6
7
8
9
10
11
12
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddLine(50, 50, 100, 100);
path.AddBezier(100, 100, 150, 50, 200, 150, 250, 100);
path.AddArc(250, 100, 50, 50, 0, 360);
path.AddLine(275, 125, 325, 75);
path.AddEllipse(350, 50, 100, 100);

Pen pen = new Pen(Color.Red, 2);
e.Graphics.DrawPath(pen, path);
}

在此示例中,创建了一个GraphicsPath对象,将其添加到图形路径中,然后使用DrawPath方法绘制路径。此示例绘制的路径由一条直线、一段贝塞尔曲线、一段弧线、一条直线和一个椭圆组成。

实现方法

  1. 创建相关变量,用于存储笔的状态,点的位置以及橡皮状态。

    1
    2
    3
    4
    5
    6
    // 用于存储所有已经画出的点 
    private ArrayList points = new ArrayList();
    // 当前线条的颜色和粗细
    private Color lineColor = Color.Black; private int lineWidth = 3;
    // 表示是否正在擦除线条
    private bool isErasing = false;
  2. 实现线条的绘制,以下是初代方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
    if (e.Button == MouseButtons.Left)
    {
    // 画线
    if (!isErasing)
    {
    Graphics g = this.CreateGraphics();
    Pen pen = new Pen(lineColor, lineWidth);
    g.DrawLine(pen, e.X, e.Y, e.X, e.Y);
    points.Add(new Point(e.X, e.Y));
    }
    // 擦除线条
    else
    {
    Graphics g = this.CreateGraphics();
    Pen pen = new Pen(this.BackColor, lineWidth);
    g.DrawLine(pen, e.X, e.Y, e.X, e.Y);
    }
    }
    }

    经检查,该方法存在以下几个问题:

  3. 在画线时,每次MouseDown事件只画了一个点,而没有实现用鼠标拖动时画出连续的线条。

  4. 在擦除线条时,MouseUp事件也应该触发擦除,否则可能出现用户按下左键后移出窗体后,线条不能被完全清除的情况。

  5. CreateGraphics方法创建的Graphics对象是临时的,并不会保存在窗体中。当窗体重绘时,先前画的图形就会被清除掉。因此,在处理Paint事件时,应该使用已经创建好的e.Graphics对象来绘制图形。

  6. 在擦除线条时,只是画一条背景色的线,并没有真正地从points列表中删除被擦除的线条。

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
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// 画线
if (!isErasing)
{
points.Add(new ArrayList());
((ArrayList)points[points.Count - 1]).Add(new Point(e.X, e.Y));
}
// 擦除线条
else
{
isErasing = false;
points.Clear();
this.Invalidate();
}
}
}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}

if (!isErasing)
{
// 添加新的点
((ArrayList)points[points.Count - 1]).Add(new Point(e.X, e.Y));

// 重绘窗体
this.Invalidate();
}
}

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// 如果没有画出任何线条,则不进行处理
if (((ArrayList)points[points.Count - 1]).Count <= 1)
{
points.RemoveAt(points.Count - 1);
}

this.Invalidate();
}
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
// 绘制所有的线条
foreach (ArrayList line in points)
{
// 如果正在擦除,则跳过当前线条
if (isErasing)
{
continue;
}

Pen pen = new Pen(lineColor, lineWidth);

// 使用GraphicsPath可以使线条更加平滑
GraphicsPath path = new GraphicsPath();
for (int i = 0; i < line.Count - 1; i++)
{
path.AddLine((Point)line[i], (Point)line[i + 1]);
}
e.Graphics.DrawPath(pen, path);
}
}

  1. 橡皮功能

    1
    2
    3
    4
    5
    6
    private void 橡皮ToolStripMenuItem_Click(object sender, EventArgs e)
    {
    // 开始擦除线条
    isErasing = true;
    this.Invalidate();
    }

    然而这就造成了一个问题,在将所有的点都清除后,(ArrayList)points[points.Count - 1]其中points.Count会变成0,所以points.Count-1就会成为-1,造成索引失败,如下图。

    这样的话,我们直接将原来的Form1_MouseMose函数中的((ArrayList)points[points.Count - 1]).Add(new Point(e.X, e.Y));改为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if(points.Count == 0)
    {
    points.Add(new ArrayList());
    }
    else
    {
    ((ArrayList)points[points.Count - 1]).Add(new Point(e.X, e.Y));

    }
  2. 修改字体颜色,这里我用的是colordialog组件可以更加直观的修改颜色。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private void 颜色ToolStripMenuItem_Click(object sender, EventArgs e)
    {
    // 设置线条颜色
    ColorDialog dialog = new ColorDialog();
    if (dialog.ShowDialog() == DialogResult.OK)
    {
    lineColor = dialog.Color;
    this.Invalidate();
    }
    }
  3. 最后是粗细调整,因为没有现成的组件,只好新建一个窗口,用来输入数字。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //form1.cs
    public Form1()
    {//初始化中要构造form2
    InitializeComponent();
    Form2 form2 = new Form2();
    this.MouseDown += new MouseEventHandler(Form1_MouseDown);
    this.MouseUp += new MouseEventHandler(Form1_MouseUp);
    this.MouseMove += new MouseEventHandler(Form1_MouseMove);
    this.Paint += new PaintEventHandler(Form1_Paint);
    }
    private void 粗细ToolStripMenuItem_Click(object sender, EventArgs e)
    {
    Form2 form2 = new Form2();
    form2.ShowDialog();

    // 获取从Form2中输入的文本
    lineWidth = form2.LineWidth;

    // 在MessageBox中显示输入的文本
    MessageBox.Show("设置的粗细为:" + lineWidth);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//form2.cs
public partial class Form2 : Form
{
public int LineWidth { get; private set; }
public Form2()
{
InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e)
{

}

private void button1_Click(object sender, EventArgs e)
{
LineWidth = Convert.ToInt32(textBox1.Text);
this.Close();
}
}
}

References