橡皮筋线条






4.74/5 (15投票s)
2005年11月2日
3分钟阅读

98236

1293
将橡皮筋线用作标尺。
引言
本文介绍了创建用于创建“橡皮筋”样式标尺的小类的过程,该标尺可用于任何项目。它使用 GDI+ 中的一项技术来替换具有许多问题的 ControlPaint.DrawReversibleLine
,我将不在此处详细介绍这些问题。请注意,此代码是使用 VS2005 编写的,但没有理由它不能在早期版本中使用,只需确保更改 Using
命令。
背景
我试图创建一个需要我绘制平面图的应用程序,这导致了一个问题,即如何向用户显示要绘制的墙的长度。我最初从众所周知的、有文档记录的 .NET 方法 DrawReversibleLine
开始,该方法位于 ControlPaint
类中。但这导致了很多问题,其中一些是世界坐标相关性、绘画问题(XOR 绘图在很多方面都有缺陷)、颜色选择(没有颜色选择)等等,但它们是主要的问题。所以我决定编写自己的类。
使用代码
我首先将可逆线代码保留在 MouseDown
、MouseMove
和 MouseUp
事件中,这可以在许多关于绘制可逆线的文章中找到,所以我不会详细介绍。简而言之,MouseDown
存储起点,MouseMove
完成设置当前点和使控件表面无效的繁重工作,以便它将重新绘制,然后最后 MouseUp
事件触发事件以通知侦听器已定义两个点。
在此橡皮筋线的实现中有几个超出常规范畴的技巧,我想分享的就是它们。
第一个是在线的中间添加一个文本标签(随着线的移动而旋转),该标签显示线的长度。第二个是该类仅绘制已更改的控件部分的方式。
文本标签(旋转文本)
这是最难实现的工作,但 GDI+ 库中的一些功能使这项任务更容易。首先要克服的是长度本身,但基本的三角学解决了这个问题
' length^2 = width^2 + height^2
length = Math.Sqrt(rect.Width ^ 2 + rect.Height ^ 2)
现在我所要做的就是将该值放到线上,并旋转以跟随该线,为此我需要旋转文本的角度。再次,三角学进行了救援,给了我以下答案
angle = Math.Atan((p1.Y - p2.Y) / (p1.X - p2.X)) * (180 / Math.PI)
现在有了 LineLength
和 Angle
函数,我将以下部分组合在一起,并将其添加到 Paint
事件(如果鼠标仍然按下)。该矩阵用于围绕线的中心点执行旋转,以便在绘制椭圆和文本时,它们实际上是在旋转的图形设备上绘制的。字符串格式仅用于确保文本位于线的中心,这比尝试在用于绘制椭圆的矩形中绘制文本要好得多。椭圆的目的是确保无论背景如何,都可以始终轻松读取文本。
Using mx As New System.Drawing.Drawing2D.Matrix
mx.Translate(midPoint.X, midPoint.Y)
mx.Rotate(Angle(_origin, _last))
e.Graphics.Transform = mx
Using sf As New StringFormat()
Dim ls As String = CInt(LineLength(_origin, _last))
Dim l As SizeF = e.Graphics.MeasureString(ls, _
_parent.Font, _parent.ClientSize, sf)
sf.LineAlignment = StringAlignment.Center
sf.Alignment = StringAlignment.Center
Dim rt As New Rectangle(0, 0, l.Width, l.Height)
rt.Inflate(3, 3)
rt.Offset(-(l.Width / 2), -(l.Height / 2))
Using backBrush As New SolidBrush(_backColor)
e.Graphics.FillEllipse(backBrush, rt)
End Using
Using foreBrush As New SolidBrush(_foreColor)
e.Graphics.DrawString(ls, _parent.Font, foreBrush, 0, 0, sf)
End Using
End Using
End Using
使用该类
实际使用该类就像将其添加到您的项目,然后根据需要实例化该类一样容易。以下代码在表单中使用该类。(在 Dispose
方法中,请务必释放该类。)注意在表单上设置的样式以防止闪烁。
Public Class Form1
Private WithEvents _ruler As MouseRuler
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or _
ControlStyles.AllPaintingInWmPaint, True)
_ruler = New MouseRuler(Form1)
End Sub
Private Sub _ruler_CaptureFinished(ByVal sender As Object, _
ByVal e As CaptureEventArgs) Handles _ruler.CaptureFinished
'todo: Add wall between points in CaptureEventArgs
End Sub
End Class
历史
迄今为止所做的更改
- 无。