切勿使用 [Control].CreateGraphics,除非您需要在特定环境中立即使用此对象。例如,在特定图形上下文中绘制时测量文本的大小。
当需要绘制持久化时,如在控件表面绘制,使用Paint事件的PaintEventArgs提供的Graphics对象(或类似事件,如ComboBox、ListBox的DrawItem事件, ListView 控件)。
您会在 SO(以及整个网络)上找到这种建议。
如何进行:
- 我们需要一个对象来存储定义手工曲线的鼠标/笔移动。
- 每次释放鼠标左键(或笔触丢失并重新获取)时,对象都需要存储新的曲线定义。
- 我们需要一个 Graphics 对象,它可以将鼠标/笔移动定义的点转换为Bezier curves(曲线组合在矢量图形中通常称为路径)。
这里,存储运动的对象是Dictionary(Of Integer, List(Of Point)),其中Key代表曲线,Value > 表示定义该曲线的点的集合。
每次按下鼠标左键时,都会创建一个新的Key,并与Key 关联一个新的List(Of Point)。
当鼠标/笔移动时,新的点位置将添加到当前曲线的List(Of Point)。
GraphicsPath 类可以使用GraphicsPath.AddCurve() 方法将List(Of Point) 集合转换为贝塞尔曲线的控制点。
此方法接受一个点数组和一个 Tension 值作为参数。 Tension 是一个介于 0 和 1 之间的值,它定义了连接点时应用于曲线的弯曲量。这里使用了0.5f 的值。
► 当我们需要在 Bitmap 上绘制图形以将结果保存到磁盘时,我们将相同的逻辑应用于从 Bitmap 对象派生的 Graphics 对象。
因此,只有一种方法用于在 Control 的表面和 Bitmap 对象上进行绘制。
此代码中的 DrawSignature(g As Graphics) 方法。
这就是它的工作原理:
重现上述过程的代码:
Private signatureObject As New Dictionary(Of Integer, List(Of Point))
Private signaturePen As New Pen(Color.Black, 4)
Private currentCurvePoints As List(Of Point)
Private currentCurve As Integer = -1
Private Sub pBoxSignature_MouseDown(sender As Object, e As MouseEventArgs) Handles pBoxSignature.MouseDown
currentCurvePoints = New List(Of Point)
currentCurve += 1
signatureObject.Add(currentCurve, currentCurvePoints)
End Sub
Private Sub pBoxSignature_MouseMove(sender As Object, e As MouseEventArgs) Handles pBoxSignature.MouseMove
If e.Button <> MouseButtons.Left OrElse currentCurve < 0 Then Return
signatureObject(currentCurve).Add(e.Location)
pBoxSignature.Invalidate()
End Sub
Private Sub btnClearSignature_Click(sender As Object, e As EventArgs) Handles btnClearSignature.Click
currentCurve = -1
signatureObject.Clear()
pBoxSignature.Invalidate()
End Sub
Private Sub btnSaveSignature_Click(sender As Object, e As EventArgs) Handles btnSaveSignature.Click
Dim signatureFileName = txtSignatureFileName.Text.Trim()
If String.IsNullOrEmpty(signatureFileName) Then Return
If currentCurve < 0 OrElse signatureObject(currentCurve).Count = 0 Then Return
Using imgSignature As Bitmap = New Bitmap(pBoxSignature.Width, pBoxSignature.Height, PixelFormat.Format32bppArgb),
g As Graphics = Graphics.FromImage(imgSignature)
DrawSignature(g)
Dim signaturePath As String = Path.Combine(Application.StartupPath, $"{signatureFileName}.png")
imgSignature.Save(signaturePath, ImageFormat.Png)
pBoxSavedSignature.Image = New Bitmap(imgSignature)
End Using
End Sub
Private Sub pBoxSignature_Paint(sender As Object, e As PaintEventArgs) Handles pBoxSignature.Paint
If currentCurve < 0 OrElse signatureObject(currentCurve).Count = 0 Then Return
DrawSignature(e.Graphics)
End Sub
Private Sub DrawSignature(g As Graphics)
g.CompositingMode = CompositingMode.SourceOver
g.CompositingQuality = CompositingQuality.HighQuality
g.SmoothingMode = SmoothingMode.AntiAlias
For Each curve In signatureObject
If curve.Value.Count < 2 Then Continue For
Using gPath As New GraphicsPath()
gPath.AddCurve(curve.Value.ToArray(), 0.5F)
g.DrawPath(signaturePen, gPath)
End Using
Next
End Sub