现在的位置: 首页 > 综合 > 正文

使用 GDI+ API 绘制橡皮带线条

2013年12月04日 ⁄ 综合 ⁄ 共 2940字 ⁄ 字号 评论关闭
文章目录

使用 GDI+ API 绘制橡皮带线条

在向您介绍这种方法之前,GUI 博士将列举诸多优势。首先,这种方法还具有附加优势,可以用作仅处理托管代码的解决方案。此外,这种机制还克服了前面介绍的 DrawReversibleLine 机制的所有缺点。

对于初学者,您不必为任何可能的难题而感到困扰,尽管它在实质上是一些极为简单的剪辑逻辑。这是因为您是在与 Form 对应的 Graphics 图面上绘图,该图面可以自动将橡皮带线条限制在 Form 的范围之内。

接下来,您还会发现自己不必为调用 PointToScreen 方法而感到烦恼。这也是因为您是在 Graphics 对象上绘图,该对象的坐标与您的工作区坐标保持同步,而在前面的例子中,您必须将这两个坐标进行同步。

最后,您会发现自己实际上控制了橡皮带线条所显示的确切颜色。实际上,在这种情况下,不管 FormBackColor 属性的值是什么,绘制出的橡皮带线条均为蓝色。而它们是否可见则是另一回事,这取决于 FormBackColor 的实际值。不过,这允许您从总体上控制屏幕输出,而在前面的例子中,橡皮带线条始终在屏幕上可见,并且不一定显示为您所选择的颜色。

要处理这种情况,您需要一个表示 Graphics 对象的成员变量。您还需要表示两种类型 System.Drawing.Pen 对象的变量,您将使用这两个变量在 Graphics 对象上绘图。最后,与前面的例子一样,您还需要表示橡皮带线条的起点和上一个终点的变量。

因此,它们是:

Private DrawingSurface As System.Drawing.Graphics

Private ErasePen As System.Drawing.Pen
Private DrawPen As System.Drawing.Pen

Private ptsStart As System.Drawing.Point
Private ptsPrevious As System.Drawing.Point

如前所述,您需要在 Form 的构造函数中实例化 Graphics 对象。而且,您还需要创建 Pen 对象;一个用于清除原来的线条,一个用于绘制新线条。因此,在调用 InitializeComponent 之后,请继续操作并将此代码添加到 Form 的构造函数中。

DrawingSurface = Me.CreateGraphics()

' Pen to erase a previous line
ErasePen = New System.Drawing.Pen(Me.BackColor)
' Pen to draw a new line
DrawPen = New System.Drawing.Pen(Color.Blue)

您的重写 OnMouseDown 方法与前面例子中的方法几乎完全相同:

Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
' Check if the left mouse button has
' generated the associated event.
If e.Button = MouseButtons.Left Then
' Store the starting point for your rubber-band line.
ptsStart.X = e.X
ptsStart.Y = e.Y
' Store the previous endpoint for your rubber-band line.
ptsPrevious = ptsStart
End If
End Sub

这时,通过该重写 OnMouseMove 方法,您可以实际绘制出橡皮带线条;正如前面的例子所示:

Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
' Check if the left mouse button is pressed.
If e.Button = MouseButtons.Left Then
' Declare a local variable for the current endpoint '
' of your rubber-band line.
Dim ptsCurrent As System.Drawing.Point
' Store the current endpoint of your rubber-band line.
ptsCurrent.X = e.X
ptsCurrent.Y = e.Y
' Draw your actual rubber-band lines.
DrawingSurface.DrawLine(ErasePen, _
ptsStart.X, ptsStart.Y, ptsPrevious.X, ptsPrevious.Y)
DrawingSurface.DrawLine(DrawPen, _
ptsStart.X, ptsStart.Y, ptsCurrent.X, ptsCurrent.Y)
' Store the current endpoint for the next call to OnMouseMove.
ptsPrevious = ptsCurrent
End If
End Sub

这与实际的代码差别不大;实际上,唯一的更改是对 DrawLine 的两次调用。但此时,当您实际开始生成并运行应用程序时,还是会发现存在相当大的区别。

因此,在 GUI 博士介绍的这三种方法(实质上,使用 R2_NOTR2_XORPEN 进行互操作是同一回事,我们将其视为一种解决方案)中,使用互操作方法似乎可以很好地满足您的需求,而这正是您所需要的方法。作为几种辅助方法,GUI 博士希望针对您的问题采用纯托管代码的其他两种方法(使用 ControlPaint.DrawReversibleLine 方法和使用 Graphics.DrawLine)能够长期解决您的问题。博士再次强调,在一般的橡皮带方案中,Graphics.DrawLine 方法优于其他两种方法。

尽管 GUI 博士提出了这几种方法,但他仍要指出,这三种方法中均存在注意事项。如果您最小化 Form 或在 Form 前面放置了其他窗口,则当您恢复为具有输入焦点的 Form 时,您将看到以下两种情况之一:您以前绘制的橡皮带线条全部消失,或覆盖窗口所隐藏的线条部分被清除。如果在最小化操作后恢复您的 Form,则系统会向您的 Form 发送一则 WM_PAINT 消息。但是,由于您不是处理 FormPaint 事件,因此在默认情况下会调用 Form.OnPaint 方法来处理 WM_PAINT 消息。但是,这会导致您 Form 的整个工作区被重绘。由于 Form.OnPaint 方法不“了解”您的橡皮带线条,因此重绘的工作区将不包含原来的内容。

如果用另一个窗口覆盖了 Form 的部分内容,然后再将输入焦点重新设为 Form,则也会发生类似的事件。如前所述,要解决这一问题,您需要使用某种技术来保留 Form 的状态,并在每次需要时重绘必需的区域。

在最简单的可能解决方案中,您可以使用重写 OnPaint
方法来执行所有的绘图操作。这就要求您在所处理的各种事件之间进行同步,以确保您不会遇到因重绘逻辑不一致而产生的任何问题。但是,从您最初的提问可以看
出,您的特定方案似乎不适合处理这种“重绘操作”。因此,GUI 博士对您的特定方案得出的结论是:这三种方法至少可以暂时解决您的问题。

抱歉!评论已关闭.