VB.Net中的自定义文本框

时间:2011-11-29 19:12:09

标签: vb.net custom-controls

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

1 个答案:

答案 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消息,这基本上使控件使其非客户区无效。

根据需要重构。