2011-11-29 96 views
1

tldr - 制作文本框的一个子类,当文本具有焦点时,文本看起来很扭曲。处理它的正确方法是什么?VB.Net中的自定义文本框

对于我公司的VB.Net应用程序,我被要求让我们的文本框像谷歌的文本框一样行事,例如,当他们有焦点和灰色边界时,他们需要在他们周围有一个蓝色的边框。不要。我已经可以通过将文本框的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

假设OnPaint是您所做的唯一更改,您是否可以发布该代码? – Timiz0r

+0

TextBox从根本上不支持打开UserPaint样式。 –

回答

2

正如汉斯提到的,文本框不会,即使它绘制其文本使用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,这基本上使控制无效它的非工作区。

根据需要重构。