带圆角的文本框
一个带有圆角的 TextBox 控件。
引言
我一直在寻找一个如何使用圆角绘制 Windows Forms TextBox 的例子,但我没有找到解决方案。 我收集了一些有用的技巧,将它们与一些逻辑结合在一起,并提出了一个令人满意的解决方案。 因为我在这个网站上找到了许多优秀的编码技巧,所以我在这里发布源代码,希望它可以对某人有用。
概述
为了实现圆角文本框,我首先尝试使用 paint override 事件,但不幸的是没有任何结果,这归因于(我认为)文本框派生自 Windows。 因此,我尝试覆盖 WM_PAINT
API,它产生了想要的结果。
接下来棘手的部分是,我最初只能在文本框的客户端区域上绘制,这导致文本框非常小。 再次,API 提出了一个解决方案:我添加了两个声明,一个用于 GetWindowDC
以获取非客户端区域的句柄,另一个用于 ReleaseDC
以在自定义绘图后进行清理。
既然我可以完全使用文本框的图形,并添加了绘制圆角矩形的代码,我仍然不完全满意,因为文本框的高度取决于字体大小。 我尝试寻找一种方法来覆盖这个“隐藏”的 autosize 事件,但没有找到可行的解决方案。 我提出了一种通过将 multiline 功能设置为 true 来更改文本框高度的方法,但我仍然相信必须有更好的方法。 因为我还添加了一个捕获 Enter 键按下事件并返回 Tab 键按下事件的方法,所以 multiline 解决方案可以满足我的需求。
由于圆角文本框控件是我最近编写的更大解决方案的一部分,您还会发现一些自定义属性,OriText
,NewText
和一个自定义事件 Texthaschanged
,它实际上是一种在 Onleave
事件发生之前检查控件文本是否已更改的方法,并且自定义属性提供对原始文本和新文本的访问。 我需要这些属性并将它们保留在控件中,也许有人会发现它们有用。
代码
好了,现在开始编写代码。
下面的过程处理按下 Enter 键的事件,以及按下“,”键的事件。 Enter 键已替换为 Tab 键,以便选择 Tab 顺序中的下一个控件。 该过程的第二部分是处理小数字符的输入并根据比利时的小数点格式化输入。
Protected Overrides Function ProcessCmdKey(ByRef msg _
As System.Windows.Forms.Message, _
ByVal keyData As System.Windows.Forms.Keys) As Boolean
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
ElseIf msg.WParam.ToInt32() = CInt(Keys.Decimal) Then
SendKeys.Send(",")
Return True
End If
End Function
下一个方法是 WM_Paint
事件的实际覆盖,其中完成了重绘。 它利用 API 函数 GetWindowDC
和 ReleaseDC
来获取控件的实际图形,包括非客户端区域。
Protected Overrides Sub WndProc(ByRef m As _
System.Windows.Forms.Message) Handles MyBase.WndProc(m)
Select Case m.Msg
Case &HF 'WM_PAINT
Dim rect As New Rectangle(0, 0, MyBase.Width, MyBase.Height)
Dim hDC As IntPtr = GetWindowDC(Me.Handle)
Dim g As Graphics = Graphics.FromHdc(hDC)
If Me.Enabled Then
g.Clear(Color.White)
Else
g.Clear(Color.FromName("control"))
End If
DrawBorder(g)
DrawText(g)
ReleaseDC(Me.Handle, hDC)
g.Dispose()
Case &H7, &H8, &H200, &H2A3
'CMB_DROPDOWN, CMB_CLOSEUP, WM_SETFOCUS,
'WM_KILLFOCUS, WM_MOUSEMOVE, 'WM_MOUSELEAVE
UpdateState()
End Select
End Sub
为了获得圆角,使用了下面显示的方法。
Private Sub TekenRondeRechthoek(ByVal g As Graphics, _
ByVal pen As Pen, ByVal rectangle As Rectangle, _
ByVal radius As Single)
Dim size As Single = (radius * 2.0!)
Dim gp As GraphicsPath = New GraphicsPath
gp.AddArc(rectangle.X, rectangle.Y, size, size, 180, 90)
gp.AddArc((rectangle.X + (rectangle.Width - size)), _
rectangle.Y, size, size, 270, 90)
gp.AddArc((rectangle.X + (rectangle.Width - size)), _
(rectangle.Y + (rectangle.Height - size)), _
size, size, 0, 90)
gp.AddArc(rectangle.X, (rectangle.Y + _
(rectangle.Height - size)), size, size, 90, 90)
gp.CloseFigure()
g.DrawPath(pen, gp)
gp.Dispose()
End Sub
完整的代码示例包含在附加的 zip 文件中。 我希望它可以对任何人有用。