Not understanding OnPaint in a control

B

BB

Hello all,

I am trying to override OnPaint in a custom textbox
control (so I can drawstring a caption, etc.). In the
code below, I get the "painting the form" message as
expected, but not the "painting the control". It also
has no effect to explicitly call txtTest.Invalidate or
txtTest.Refresh. Am I missing something simple here?
Any push in the right direction is appreciated.

BB

Public Class Form1
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "
#End Region

Private Sub Form1_Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles MyBase.Load
Dim txtTest As New MyTextBox
Controls.Add(txtTest)
End Sub

Protected Overrides Sub onpaint(ByVal e As
PaintEventArgs)
MessageBox.Show("Painting the form...")
End Sub

End Class

Public Class MyTextBox
Inherits TextBox

Protected Overrides Sub onpaint(ByVal e As
PaintEventArgs)
MessageBox.Show("Painting the textbox...")
End Sub

End Class
 
H

Herfried K. Wagner [MVP]

* "BB said:
I am trying to override OnPaint in a custom textbox
control (so I can drawstring a caption, etc.). In the
code below, I get the "painting the form" message as
expected, but not the "painting the control". It also

Call 'MyBase.OnPaint(e)' in the handler on 'Form1'.
 
O

One Handed Man [ OHM# ]

Post your code . . .



Hello all,

I am trying to override OnPaint in a custom textbox
control (so I can drawstring a caption, etc.). In the
code below, I get the "painting the form" message as
expected, but not the "painting the control". It also
has no effect to explicitly call txtTest.Invalidate or
txtTest.Refresh. Am I missing something simple here?
Any push in the right direction is appreciated.

BB

Public Class Form1
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "
#End Region

Private Sub Form1_Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles MyBase.Load
Dim txtTest As New MyTextBox
Controls.Add(txtTest)
End Sub

Protected Overrides Sub onpaint(ByVal e As
PaintEventArgs)
MessageBox.Show("Painting the form...")
End Sub

End Class

Public Class MyTextBox
Inherits TextBox

Protected Overrides Sub onpaint(ByVal e As
PaintEventArgs)
MessageBox.Show("Painting the textbox...")
End Sub

End Class

Regards - OHM# (e-mail address removed)
 
B

Brian

Basically, you're not allowed to paint on the textbox. Imagine the chaos if
a user tries to highlight, cut or copy your text.
You'll have to write your own textbox to do custom drawing. That's what I
did.
 
B

BB

There's nothing more to it than what's below. I've since
made sure I'm calling mybase.onpaint in both the form and
the textbox classes, but that hasn't changed anything.

BB
 
B

BB

I've since tried that, but it hasn't changed. And if I'm
understanding this right, shouldn't the textbox be
*getting* the paint event when the form is invalidated
and forced to repaint (which I presume the form should
then pass along to its controls)?

BB
 
H

Herfried K. Wagner [MVP]

* said:
Still nothing...

Add 'MyBase.OnPaint(e)' to both of your 'OnPaint' methods. But I don't
understand your code -- one of the method is defined for the form, the
other for the textbox. What exactly do you want to archieve?
 
B

BB

I've added the calls to the base class in each case, but
still nothing. Per Brian elsewhere in this thread,
you're not allowed to get the paint message in the
textbox, but I'm still not sure why.

My code in both the form and the textbox is for
diagnostics only, to understand which events are firing
and when.

What I'm really trying to do is create a textbox that 1)
Has its own caption (without incurring the overhead of an
additional container control that is label + textbox,
e.g. do a straight "drawstring" instead) and 2) Has the
background hatched in a way to indicate that this is
a "required" input field.

I'm thinking the cleanest way to do this is to hook
OnPaint for the textbox, then do my extra stuff whenever
the control is painted. I know there are other ways to
skin this cat, but I'd love to understand why what I'm
trying to do won't work.

Any additional thought is appreciated.

Thanks,

BB
 
B

BB

Brian, thanks for your comments. But, I still don't
understand. I know that hooking the paint event is
scary, but if it were disallowed altogether I would think
that would be true for *any* control, and in that case
why would there even be an OnPaint accessible at the
control level?
 
C

Codemonkey

In order for the OnPaint method to be called, you need to tell the control
to be drawn manually instead of by the OS in the constructor of the control.

Example:
-------------------
Public Class MyTextBox
Inherits TextBox

Public Sub New()
MyBase.New()

' Set the UserPaint Style
Me.SetStyle(ControlStyles.UserPaint, True)

End Sub

' OnPaint should now be called
Protected Overrides Sub onpaint(ByVal e As PaintEventArgs)

' Make sure to call the base paint
Mybase.OnPaint(e)
End Sub

End Class
-------------------



You will still need to be careful when painting the textbox because of the
way it paints text and selected text. Refer to
microsoft.public.dotnet.framework.drawing if you need help with the drawing
methods.

Hope this helps,

Trev.
 
B

BB

Trev, this is brilliant, thanks. I made the change and
am now hooking the event the way I want. You're right
that now I've got a bunch of weirdness with how the
control operates (for starters, the font is very odd, the
standard control keys don't work within the form, etc.),
but you've got me headed in the right direction, and I'll
check out microsoft.public.dotnet.framework.drawing.

Thanks,

Bill
 
B

Brian

If you do what codemonkey suggests and use owner drawn
style then you'll have to paint the entire text box
yourself. Do you know what that entails? You'll have to
repaint the client area every time you receive a paint
message. You have make sure that you have the correct
lines displayed and if the some of the text is
highlighted or not (there is much more to consider). This
shouldn't be taken lightly. Microsoft makes getting the
Onpaint method difficult for a reason, it's because
everything must fit together in order to make a text box
work properly. If you paint the text in the wrong
position or with the wrong font size then it won't work.

Lastly, if want a text box that will display only one
line of text then you should create your own. It isn't
that hard and you can use any background you want.
 
C

Codemonkey

then you'll have to paint the entire text box
yourself. Do you know what that entails?

If you call the MyBase.OnPaint() method, then this will draw the textbox
automatically (text, highlights and all). Granted, you'll have to take care
that the extra graphics don't overshadow the textbox's own graphics.
You'll have to repaint the client area
every time you receive a paint message.

Isn't this what you're supposed to do every time the client area is
invalidated?
Microsoft makes getting the
Onpaint method difficult for a reason,

They don't make the OnPaint method difficult for any reason. The reason why
OnPaint isn't called by default is because the UserPaint style is False by
default to allow the operating system to draw the textbox. I don't know the
exact reason for this, but I assume that it is to let the OS draw special
effects (like Windows XP themes etc.) or because it might be slightly faster
(correct me if I'm wrong).


HTH,

Trev.
 
B

Brian

Have you tried your solution? If you did then you would realize that calling
MyBase.OnPaint for the text box doesn't redraw ANY graphics in the client
area. It's your responsiblity to do that and not the controls. Calling
MyBase.OnPaint isn't going to help. The text box is a precision control and
that's why you shouldn't do your own painting. I think you should try it and
see for yourself. I just don't think you understand why you shouldn't trap
the OnPaint event for the text box and paint it yourself.
I guess until you try it you won't understand what I'm mean. Good Luck
 
B

Brian

One last thing, have you noticed that when you inherit from a textbox class
that the base class Paint event has been ommited?
Why is that?
 
C

Codemonkey

Yeah, I have tried it. It seemed to paint a normal textbox. I've tried it
again to make sure and actully typed something in it and it doesn't seem to
want to work too well. Sorry for the Dud Solution.

After a bit of research, it looks like this problem is yet another result of
Microsoft taking a shortcut with the implementation of windows forms. The
TextBox control is actually a wrapper around an unmanaged windows class
(just like the date time picker - it still has no back color property).
Controls like this go against the way things should be done in .net. Calling
a base OnPaint *should* paint the base control's default look - otherwise
there's no point in exposing it to derived classes.

I guess a better way around this is to Override the WndProc method and
process the paint messages there.

Sorry for not researching my solution a bit more. I've seen the same
solution posted many times before and nobody wrote back mentioning that they
had any problems with it.

Trev
 
A

Armin Zingler

Brian said:
One last thing, have you noticed that when you inherit from a textbox
class that the base class Paint event has been ommited?
Why is that?

Please correct me if I am wrong: A textbox is a "native" window control. The
Framework does not have to paint it, but it calls the default window
procedure for the textbox, so Win does the painting. OnPaint is not called.
 
C

Codemonkey

Because they hid it to cover up the fact that the textbox is a botch job ;)

You can still handle it, but it has the same requirement (UserPaint) and
effect as overriding the OnPaint method.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top