שתף באמצעות


Where paint event of TextBox?

Question

Tuesday, January 6, 2015 1:51 PM | 1 vote

Hi, I can't find the paint event of TextBox.

I try to use:

AddHandler TextBox1.Paint, AddressOf TextBoxPaint

 Private Sub TextBoxPaint(sender As Object, e As PaintEventArgs)
        e.Graphics.DrawLine(Pens.Black, 1, 1, 10, 10)
End Sub

But the code not call the Sub TextBoxPaint

All replies (11)

Tuesday, January 6, 2015 2:13 PM ✅Answered

According to the docs the paint event is "not relevant" for textbox.

What is it you want to do exactly that you need to draw in the text box? Maybe there is a better control to use. Maybe an RTB which lets you do more?


Wednesday, January 7, 2015 2:13 PM ✅Answered | 4 votes

Textbox is not really meant to be painted and so does not perform well when you try to force it.

You can inherit text box as below:

Public Class LinedTextBox
    Inherits TextBox

    Const WM_PAINT = &HF
    Const WM_NCPAINT = &H85
    Const WM_COMMAND = &H111
    Const WM_USER = &H400
    Const WM_REFLECT = WM_USER + &H1C00

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.WndProc(m)

        Select Case m.Msg
            Case WM_REFLECT + WM_COMMAND, WM_PAINT, WM_NCPAINT
                PaintLines(m.HWnd)
        End Select
    End Sub

    Private Sub PaintLines(handle As IntPtr)
        Using g As Graphics = Graphics.FromHwnd(handle)
            For i = 0 To Me.Lines.Count - 1
                Dim pt As Point = Me.GetPositionFromCharIndex(Me.GetFirstCharIndexFromLine(i))
                Dim txt As String = Me.Lines(i)
                If txt.Length = 0 Then
                    txt = " "
                End If
                pt.Offset(0, TextRenderer.MeasureText(txt, Me.Font).Height)
                g.DrawLine(Pens.Red, 0, pt.Y, Me.Width, pt.Y)
            Next
        End Using
    End Sub

End Class

or if you already have textbox's on your form that you don't wish to remove and replace then you can simply add a nativewindow class to intercept the messages as below

Public Class TextBoxLines
    Inherits NativeWindow

    Const WM_PAINT = &HF
    Const WM_NCPAINT = &H85
    Const WM_COMMAND = &H111
    Const WM_USER = &H400
    Const WM_REFLECT = WM_USER + &H1C00

    Private Owner As TextBox

    Public Sub New(textbox As TextBox)
        Me.Owner = textbox
        Me.AssignHandle(textbox.Handle)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        MyBase.WndProc(m)

        Select Case m.Msg
            Case WM_REFLECT + WM_COMMAND, WM_PAINT, WM_NCPAINT
                PaintLines(m.HWnd)
        End Select

    End Sub

    Private Sub PaintLines(handle As IntPtr)
        Using g As Graphics = Graphics.FromHwnd(handle)
            For i = 0 To Owner.Lines.Count - 1
                Dim pt As Point = Owner.GetPositionFromCharIndex(Owner.GetFirstCharIndexFromLine(i))
                Dim txt As String = Owner.Lines(i)
                If txt.Length = 0 Then
                    txt = " "
                End If
                pt.Offset(0, TextRenderer.MeasureText(txt, Owner.Font).Height)
                g.DrawLine(Pens.Red, 0, pt.Y, Owner.Width, pt.Y)
            Next
        End Using
    End Sub

End Class

You would then simply create an instance of this nativewindow class in the forms constructor passing a reference to the textbox you wish to associate it with:

    Dim liner As TextBoxLines

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.liner = New TextBoxLines(Me.TextBox1)

    End Sub

Mick Doherty
http://dotnetrix.co.uk
http://glassui.codeplex.com


Tuesday, January 6, 2015 2:42 PM

I wanted to draw a lines 
RTB does not want to draw too


Tuesday, January 6, 2015 2:51 PM

I wanted to draw a lines 
RTB does not want to draw too

You can underline the font in a RTB if that is what you want. Can you show a picture of what you want? Maybe you need to use a graphics control like picturebox if it is just a small piece of text.


Tuesday, January 6, 2015 3:47 PM

I wanted to selected for eyes user all lines. That would not miss the unnecessary lines at the end of the field. 

And the user can see and remove unnecessary empty lines. But do implement from Control or PictureBox very long time.


Tuesday, January 6, 2015 4:49 PM

I wanted to selected for eyes user all lines. That would not miss the unnecessary lines at the end of the field. 

And the user can see and remove unnecessary empty lines. But do implement from Control or PictureBox very long time.

This is all I can come up with if you can work out how to draw it in every event. Keep in mind the second time you draw over the first time it erases - that's the reversible part. I was trying to set a flag for the first paint and then draw it twice but there are too many events erasing the textbox.

Seems there may be a way to get to the paint event or create one. I don't know. If you unmark my first post as the answer you may get more looks. Or post again and show your picture.

    Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
        Dim p1 As Point = PointToScreen(New Point(TextBox1.Left, TextBox1.Top))
        For y = p1.Y + 30 To p1.Y + 100 Step 30
            ControlPaint.DrawReversibleLine(New Point(p1.X + 10, y), New Point(p1.X + TextBox1.Width - 10, y), Color.White)
        Next
   End Sub


Wednesday, January 7, 2015 12:38 PM | 1 vote

There is no paint event of course for a TextBox. But you can paint in it using graphics and the TextBox's handle.

The real issue is that different font sizes may not provide accurate information for drawing the lines. For example the font "Book Antiqua" (or probably any other font) with a font size of 11 seems to work fine with regard to the information returned by MeasureString for the vertical line "|" character or "M" or "W" which I believe are the tallest characters able to be measured. But if you alter the font size to 8 then after some numbers of lines the characters are becoming overdrawn by lines.

Although I believe if you could get the Carets height then always drawing the lines correctly should be no issue since you could probably figure out how to perform the line drawings for the Carets height. But I've no idea how to get a carets height. Perhaps if you could get a lines height that could work too.

Option Strict On

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Left = 10
        Me.Top = 10
        TextBox1.Font = New Font("Book Antiqua", 11)
        TextBox1.Anchor = CType(AnchorStyles.Bottom + AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top, AnchorStyles)
    End Sub

    Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
        DrawLinesInTextBox1()
    End Sub

    Private Sub TextBox1_TextChanged(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown
        DrawLinesInTextBox1()
    End Sub

    Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        DrawLinesInTextBox1()
    End Sub

    Private Sub DrawLinesInTextBox1()
        TextBox1.Refresh()
        If TextBox1.Text <> "" Then
            Using g As Graphics = Graphics.FromHwnd(TextBox1.Handle)
                Dim HeightToUse As Single = g.MeasureString("|", TextBox1.Font).Height
                For i = 1 To TextBox1.Lines.Count
                    g.DrawLine(Pens.Red, 0, i * HeightToUse, TextBox1.ClientRectangle.Width, i * HeightToUse)
                Next
            End Using
        End If
    End Sub

End Class

La vida loca


Wednesday, January 7, 2015 12:50 PM

Good one Monkey.

I came up with making a class to get a paint event. But, I am drawing lines but as you press enter apparently the part that draws the text is erasing the lines.

Not sure how to fix that?

Public Class Form4
    Private WithEvents TextEx1 As New TextBoxEX

    Private Sub Form4_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Controls.Add(TextEx1)
        TextEx1.Multiline = True
        TextEx1.Dock = DockStyle.Fill
    End Sub
    Public Class TextBoxEX
        Inherits TextBox

        Public Sub New()
            MyBase.SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer, True)
        End Sub

        Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

            MyBase.OnPaint(e)

            For y = 30 To Me.Height Step 30
                e.Graphics.DrawLine(Pens.Red, 10, y, Me.Width - 10, y)
            Next

        End Sub
    End Class
End Class


Wednesday, January 7, 2015 1:41 PM

I tried the same thing, for a few hours. I couldn't figure it out. So finally I went the route I posted.

I was even providing the created textbox with handlers to try to get it to refresh and everything. Which when the code I posted worked made me wonder even more why the class wouldn't work.

I even used ControlPaint.DrawGrid and the grid would go away. I suspect it has to do with not handling the drawing of both the text and lines within the TextBox perhaps but I gave up! :)

La vida loca


Wednesday, January 7, 2015 7:09 PM

So can you draw on most any control by creating a graphics object with the handle?

        Using g As Graphics = Graphics.FromHwnd(TextBox1.Handle)
   


Wednesday, January 7, 2015 9:19 PM

So can you draw on most any control by creating a graphics object with the handle?

        Using g As Graphics = Graphics.FromHwnd(TextBox1.Handle)
   

Sure. The problem is not painting, the problem is persisting the painting (i.e. painting every time the control needs it, which is not always obvious).

Whenever there is a Paint method exposed for use then it should be used, along with the graphics object passed with it.

Mick Doherty
http://dotnetrix.co.uk
http://glassui.codeplex.com