tldr - 制作了Textbox的子类,文本在有焦点时看起来很麻烦。处理它的正确方法是什么?
对于我公司的VB.Net应用程序,我被要求让我们的文本框表现得像Google的文本框,即当他们有焦点时他们需要有一个蓝色边框,当他们有焦点时他们需要一个灰色边框不。我已经可以通过将文本框的BorderStyle设置为“None”来完成此操作,然后在表单的Paint事件中绘制相应的矩形。但是,我必须为我使用的每个文本框执行此操作。我们的应用程序有很多。毋庸置疑,这是一种痛苦,我宁愿拥有一条我可以调用的代码。
所以我认为我有两个选择;我可以创建一个包含单个文本框的用户控件,该文本框使用上面的方法,或者我可以编写自己的继承自TextBox类的类,并使此行为成为标准。我选择使用后一种方法,并通过重写OnPaint方法,我已经实现了所需的行为。但现在我遇到了一些新的陷阱。
我遇到的主要问题是,当文本框具有焦点时,文本框中的文本无法正确呈现。文本采用不同的字体,显示为粗体,突出显示看起来很难看。如果文本框失去焦点,则文本看起来正确。我怀疑我需要以不同方式处理突出显示文本的绘图,但我不确定我需要做什么。我是在OnPaint方法中处理它还是需要在其他地方捕获它?我是否需要完全放弃这种方法并让用户控制?
奖金问题:对于有自定义文本框经验的人,是否有任何我需要了解的提示或陷阱?这是我第一次制作自定义控件,所以我真的不知道会发生什么。
编辑:忘了提到我能够覆盖OnPaint,因为我将UserPaint标志设置为true。我猜这很明显,但我只想彻底。
edit2:这是完整的课程。
Imports System.Drawing
Public Class MyCustomTextBox
Inherits TextBox
Public Sub New()
MyBase.New()
Me.BorderStyle = BorderStyle.None
SetStyle(ControlStyles.UserPaint, True)
End Sub
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
'I want these textboxes to highlight all text by default
Me.SelectAll()
MyBase.OnGotFocus(e)
End Sub
Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
Me.SelectionLength = 0
MyBase.OnLostFocus(e)
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim p As Pen = Nothing
'MyBase.OnPaint(e)
e.Graphics.FillRectangle(Brushes.White, Me.ClientRectangle)
If Me.Focused Then
p = New Pen(Brushes.CornflowerBlue)
Else
p = New Pen(Brushes.Gainsboro)
End If
e.Graphics.DrawRectangle(p, 0, 0, Me.ClientSize.Width - 1, Me.ClientSize.Height - 1)
e.Graphics.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), Me.ClientRectangle)
End Sub
End Class
答案 0 :(得分:2)
正如Hans所说,TextBox在绘制文本时甚至不使用OnPaint方法。
一种方法是在WM_NCPAINT消息中绘制控件的3D边框。我不会声称它完全没有闪烁:
Imports System.Runtime.InteropServices
Public Class TextBoxWithBorder
Inherits TextBox
Public Const WM_NCPAINT As Integer = &H85
<Flags()> _
Private Enum RedrawWindowFlags As UInteger
Invalidate = &H1
InternalPaint = &H2
[Erase] = &H4
Validate = &H8
NoInternalPaint = &H10
NoErase = &H20
NoChildren = &H40
AllChildren = &H80
UpdateNow = &H100
EraseNow = &H200
Frame = &H400
NoFrame = &H800
End Enum
<DllImport("User32.dll")> _
Public Shared Function GetWindowDC(ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function RedrawWindow(hWnd As IntPtr, lprcUpdate As IntPtr, hrgnUpdate As IntPtr, flags As RedrawWindowFlags) As Boolean
End Function
Public Sub New()
MyBase.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
End Sub
Protected Overrides Sub OnResize(e As System.EventArgs)
MyBase.OnResize(e)
RedrawWindow(Me.Handle, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Frame Or RedrawWindowFlags.UpdateNow Or RedrawWindowFlags.Invalidate)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = WM_NCPAINT Then
Dim hDC As IntPtr = GetWindowDC(m.HWnd)
Using g As Graphics = Graphics.FromHdc(hDC)
If Me.Focused Then
g.DrawRectangle(Pens.CornflowerBlue, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
Else
g.DrawRectangle(Pens.Gainsboro, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
End If
g.DrawRectangle(SystemPens.Window, New Rectangle(1, 1, Me.Width - 3, Me.Height - 3))
End Using
ReleaseDC(m.HWnd, hDC)
End If
End Sub
End Class
我重写OnResize事件以发送RedrawWindow消息,这基本上使控件使其非客户区无效。
根据需要重构。