UserPaint a TextBox - issue with WM_PRINT

B

Bob Dankert

I am creating a custom textbox that requires me to handle the OnPaint
mechanism. Currently, I am using WM_PRINT to draw the text in the textbox
and am running into a problem. The text will never follow the font that is
specified by the textbox, it is always the same no matter what I change it
to. Is there some way to get WM_PRINT to use the font that the textbox is
set with?

I stripped down the code for my class to the following to show the issue:
using System;
using System.Windows.Forms;
namespace TransTextBox
{
public class MyTextBox : TextBox
{
public MyTextBox()
{
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint |
ControlStyles.DoubleBuffer | ControlStyles.SupportsTransparentBackColor,
true);
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
System.IntPtr hdc = e.Graphics.GetHdc();
this.SetStyle(ControlStyles.UserPaint, false);
try
{
const Int32 WM_PRINT = 0x0317;
const long PRF_CLIENT = 0x00000004;
Message m = Message.Create(this.Handle, WM_PRINT, hdc, new
System.IntPtr(PRF_CLIENT));
this.WndProc(ref m);
}
catch { }
finally
{
e.Graphics.ReleaseHdc(hdc);
this.SetStyle(ControlStyles.UserPaint, true);
}
}
}
}

I appreciate any help - thanks.

Bob Dankert
 
B

Bob Dankert

I'm not really sure how the code seems similar?? After looking at that code
sample, it seems like it is creating bitmaps to display everything - I
didn't see any mention of WM_PRINT.

The code is certainly creative as far as accomplishing a transparent
textbox, though.

Anyways, I'm still having the same issue.

Bob
 
J

Jeffrey Tan[MSFT]

Hi Bob,

Thanks for your post.

Yes, I can reproduce out your problem on my side with your code snippet.

If we use Reflector to view Control.Font property, we can see the root
cause:

public virtual void set_Font([MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType="", MarshalTypeRef=typeof(Control.ActiveXFontMarshaler),
MarshalCookie="")] Font value)
{
Font font1 = (Font) this.Properties.GetObject(Control.PropFont);
Font font2 = this.Font;
bool flag1 = false;
if (value == null)
{
if (font1 != null)
{
flag1 = true;
}
}
else if (font1 == null)
{
flag1 = true;
}
else
{
flag1 = !value.Equals(font1);
}
if (flag1)
{
this.Properties.SetObject(Control.PropFont, value);
if
(this.Properties.ContainsObject(Control.PropFontHandleWrapper))
{
this.Properties.SetObject(Control.PropFontHandleWrapper,
null);
}
if (!font2.Equals(value))
{
if
(this.Properties.ContainsInteger(Control.PropFontHeight))
{
this.Properties.SetInteger(Control.PropFontHeight,
(value == null) ? -1 : value.Height);
}
this.OnFontChanged(EventArgs.Empty);
}
else if (this.IsHandleCreated &&
!this.GetStyle(ControlStyles.UserPaint))
{
this.SendMessage(0x30, this.FontHandle, 0);
}
}
}
Yes, in the last line, Winform code determines if UserPaint is enabled. If
it is true, it will not send 0x30(WM_SETFONT) message to refresh the
painting.

For this issue, we can explicitly send a 0x30(WM_SETFONT) message to enable
the font. Like this:

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr
wParam, IntPtr lParam);

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
SendMessage(this.Handle, 0x30, this.Font.ToHfont(), IntPtr.Zero);
System.IntPtr hdc = e.Graphics.GetHdc();
this.SetStyle(ControlStyles.UserPaint, false);
try
{
const Int32 WM_PRINT = 0x0317;
const long PRF_CLIENT = 0x00000004;
Message m = Message.Create(this.Handle, WM_PRINT, hdc, new
System.IntPtr(PRF_CLIENT));
this.WndProc(ref m);
}
catch { }
finally
{
e.Graphics.ReleaseHdc(hdc);
this.SetStyle(ControlStyles.UserPaint, true);
}
}
This works well on my side.

Additionally, if you want to do the painting ourselves, please refer to the
link below:
"27.15 When I set a TextBox to Readonly or set Enabled to false, the text
color is gray. How can I force it to be the color specified in the
ForeColor property of the TextBox."
http://www.syncfusion.com/FAQ/WindowsForms/FAQ_c94c.aspx

Hope this helps
================================================================
Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
B

Bob Dankert

Thanks a lot for the help, Jeffrey. I never realized you could view the
code of the framework - this is definately nice to know! Out of curiousity,
which tools did you use to view the code of the Font property?

Thanks,

Bob

"Jeffrey Tan[MSFT]" said:
Hi Bob,

Thanks for your post.

Yes, I can reproduce out your problem on my side with your code snippet.

If we use Reflector to view Control.Font property, we can see the root
cause:

public virtual void set_Font([MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType="", MarshalTypeRef=typeof(Control.ActiveXFontMarshaler),
MarshalCookie="")] Font value)
{
Font font1 = (Font) this.Properties.GetObject(Control.PropFont);
Font font2 = this.Font;
bool flag1 = false;
if (value == null)
{
if (font1 != null)
{
flag1 = true;
}
}
else if (font1 == null)
{
flag1 = true;
}
else
{
flag1 = !value.Equals(font1);
}
if (flag1)
{
this.Properties.SetObject(Control.PropFont, value);
if
(this.Properties.ContainsObject(Control.PropFontHandleWrapper))
{
this.Properties.SetObject(Control.PropFontHandleWrapper,
null);
}
if (!font2.Equals(value))
{
if
(this.Properties.ContainsInteger(Control.PropFontHeight))
{
this.Properties.SetInteger(Control.PropFontHeight,
(value == null) ? -1 : value.Height);
}
this.OnFontChanged(EventArgs.Empty);
}
else if (this.IsHandleCreated &&
!this.GetStyle(ControlStyles.UserPaint))
{
this.SendMessage(0x30, this.FontHandle, 0);
}
}
}
Yes, in the last line, Winform code determines if UserPaint is enabled. If
it is true, it will not send 0x30(WM_SETFONT) message to refresh the
painting.

For this issue, we can explicitly send a 0x30(WM_SETFONT) message to
enable
the font. Like this:

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr
wParam, IntPtr lParam);

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
SendMessage(this.Handle, 0x30, this.Font.ToHfont(), IntPtr.Zero);
System.IntPtr hdc = e.Graphics.GetHdc();
this.SetStyle(ControlStyles.UserPaint, false);
try
{
const Int32 WM_PRINT = 0x0317;
const long PRF_CLIENT = 0x00000004;
Message m = Message.Create(this.Handle, WM_PRINT, hdc, new
System.IntPtr(PRF_CLIENT));
this.WndProc(ref m);
}
catch { }
finally
{
e.Graphics.ReleaseHdc(hdc);
this.SetStyle(ControlStyles.UserPaint, true);
}
}
This works well on my side.

Additionally, if you want to do the painting ourselves, please refer to
the
link below:
"27.15 When I set a TextBox to Readonly or set Enabled to false, the text
color is gray. How can I force it to be the color specified in the
ForeColor property of the TextBox."
http://www.syncfusion.com/FAQ/WindowsForms/FAQ_c94c.aspx

Hope this helps
================================================================
Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
L

Lloyd Dupont

I see you've got your answer!

Anyway here is where is the WM_PRINT message in
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case Win32.WM_PAINT:
myPaintedFirstTime = true;
if (!myUpToDate || !myCaretUpToDate)
GetBitmaps(); =========>>>> Win32.CaptureWindow(this, myBitmap);
.......
 
J

Jeffrey Tan[MSFT]

Hi Bob,

I am glad I can help you. Actually, I got the "source code" from Reflector.
Reflector is a decompiler which decompiles the IL code in assembly into
C#, VB.net or any other high level .net language.

So we can use it to view the .Net Framework source code with it. It is
definitely a must have tool for .Net developer. You can get this tool from
http://www.aisto.com/roeder/dotnet/

Hope this helps

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
B

Bob Dankert

Lloyd - I only see the code handling the WM_PAINT (paint, not print) message in that code???
I see you've got your answer!

Anyway here is where is the WM_PRINT message in
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case Win32.WM_PAINT:
myPaintedFirstTime = true;
if (!myUpToDate || !myCaretUpToDate)
GetBitmaps(); =========>>>> Win32.CaptureWindow(this, myBitmap);
.......
 
L

Lloyd Dupont

as you see in WM_PAINT there is a call to GetBitmaps().
I write (as an hint) GetBimtap next to it.
That means, if you look in the code of GetBitmap() you will see it callls Win32.CaptureWindow() that send the WM_PRINT message to the control.

On top of that code "handle" WM_PAINT, yes, but how do you think it *actually* does the painting?
By "sending" WM_PRINT to the win32 control, hey!



Lloyd - I only see the code handling the WM_PAINT (paint, not print) message in that code???
I see you've got your answer!

Anyway here is where is the WM_PRINT message in
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case Win32.WM_PAINT:
myPaintedFirstTime = true;
if (!myUpToDate || !myCaretUpToDate)
GetBitmaps(); =========>>>> Win32.CaptureWindow(this, myBitmap);
.......
 

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