Difference between 'this.CreateGraphics()' and 'Graphics.FromHdc(hDC);'

P

pigeonrandle

Hi,
Please pity me, i am on a dial-up connection for the first time in 5
years :( !

Does anyone know how the resulting Graphics objects differ ...? What i
really mean is can someone explain it to me please?

A)
[DllImport("user32.dll")] protected static extern IntPtr GetWindowDC
(IntPtr hWnd );
hDC = GetWindowDC(this.Handle);
g_dc = Graphics.FromHdc(hDC);

B)
Graphics g = this.CreateGraphics();

You have my thanks and full attention,
James Randle.
 
P

pigeonrandle

The code i am looking at (if this helps) is attached below (from
http://www.codeproject.com/cs/combobox/ComboBox_appears_flat.asp).

In the WM_NC_PAINT message handler, a graphics object is already
created, but then SendPrintClientMsg() is called which seems to do the
same work again (in reverse - getting the hDC from
this.CreateGraphics). Is this mad, or more likely, am i missing
something?

....

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;

namespace DrawFlat
{
[ToolboxBitmap(typeof(System.Windows.Forms.ComboBox))]
public class FlatComboBox: ComboBox
{
#region ComboInfoHelper
internal class ComboInfoHelper
{
[DllImport("user32")]
private static extern bool GetComboBoxInfo(IntPtr hwndCombo, ref
ComboBoxInfo info);

#region RECT struct
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion

#region ComboBoxInfo Struct
[StructLayout(LayoutKind.Sequential)]
private struct ComboBoxInfo
{
public int cbSize;
public RECT rcItem;
public RECT rcButton;
public IntPtr stateButton;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
#endregion

public static int GetComboDropDownWidth()
{
ComboBox cb = new ComboBox();
int width = GetComboDropDownWidth(cb.Handle);
cb.Dispose();
return width;
}
public static int GetComboDropDownWidth(IntPtr handle)
{
ComboBoxInfo cbi = new ComboBoxInfo();
cbi.cbSize = Marshal.SizeOf(cbi);
GetComboBoxInfo(handle, ref cbi);
int width = cbi.rcButton.Right - cbi.rcButton.Left;
return width;
}
}
#endregion

public const int WM_ERASEBKGND = 0x14;
public const int WM_PAINT = 0xF;
public const int WM_NC_PAINT = 0x85;
public const int WM_PRINTCLIENT = 0x318;
private static int DropDownButtonWidth = 17;

[DllImport("user32.dll", EntryPoint="SendMessageA")]
public static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr
wParam, object lParam);

[DllImport("user32")]
public static extern IntPtr GetWindowDC (IntPtr hWnd );

[DllImport("user32")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC );

static FlatComboBox()
{
DropDownButtonWidth = ComboInfoHelper.GetComboDropDownWidth() + 2;
}

public FlatComboBox()
: base()
{
this.SetStyle(ControlStyles.DoubleBuffer, true);
}

protected override void OnSelectedValueChanged(EventArgs e)
{
base.OnSelectedValueChanged (e);
this.Invalidate();
}

protected override void WndProc(ref Message m)
{
if (this.DropDownStyle == ComboBoxStyle.Simple)
{
base.WndProc(ref m);
return;
}

IntPtr hDC = IntPtr.Zero;
Graphics gdc = null;
switch (m.Msg)
{
case WM_NC_PAINT:
hDC = GetWindowDC(this.Handle);
gdc = Graphics.FromHdc(hDC);
SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
SendPrintClientMsg(); // send to draw client area
PaintFlatControlBorder(this, gdc);
m.Result = (IntPtr) 1; // indicate msg has been processed
ReleaseDC(m.HWnd, hDC);
gdc.Dispose();

break;
case WM_PAINT:
base.WndProc(ref m);
// flatten the border area again
hDC = GetWindowDC(this.Handle);
gdc = Graphics.FromHdc(hDC);
Pen p = new Pen((this.Enabled? BackColor:SystemColors.Control),
2);
gdc.DrawRectangle(p, new Rectangle(2, 2, this.Width-3,
this.Height-3));
PaintFlatDropDown(this, gdc);
PaintFlatControlBorder(this, gdc);
ReleaseDC(m.HWnd, hDC);
gdc.Dispose();

break;
default:
base.WndProc(ref m);
break;
}
}
private void SendPrintClientMsg()
{
// We send this message for the control to redraw the client area
Graphics gClient = this.CreateGraphics();
IntPtr ptrClientDC = gClient.GetHdc();
SendMessage(this.Handle, WM_PRINTCLIENT, ptrClientDC, 0);
gClient.ReleaseHdc(ptrClientDC);
gClient.Dispose();
}

private void PaintFlatControlBorder(Control ctrl, Graphics g)
{
Rectangle rect = new Rectangle(0, 0, ctrl.Width, ctrl.Height);
if (ctrl.Focused == false || ctrl.Enabled == false )
ControlPaint.DrawBorder(g, rect, SystemColors.ControlDark,
ButtonBorderStyle.Solid);
else
ControlPaint.DrawBorder(g, rect, Color.Black,
ButtonBorderStyle.Solid);
}
public static void PaintFlatDropDown(Control ctrl, Graphics g)
{
Rectangle rect = new Rectangle(ctrl.Width-DropDownButtonWidth, 0,
DropDownButtonWidth, ctrl.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}

protected override void OnLostFocus(System.EventArgs e)
{
base.OnLostFocus(e);
this.Invalidate();
}

protected override void OnGotFocus(System.EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize (e);
this.Invalidate();
}

}
}
 
P

Peter Duniho

pigeonrandle said:
The code i am looking at (if this helps) is attached below (from
http://www.codeproject.com/cs/combobox/ComboBox_appears_flat.asp).

In the WM_NC_PAINT message handler, a graphics object is already
created, but then SendPrintClientMsg() is called which seems to do the
same work again (in reverse - getting the hDC from
this.CreateGraphics). Is this mad, or more likely, am i missing
something?

I don't know. The whole thing does look kind of odd to me...presumably, the
code is like this because some of the code was originally unmanaged? I
can't imagine writing C# code like this from scratch.

From http://msdn2.microsoft.com/en-us/library/ms535950.aspx it's apparent to
me that FromHdc does what you'd think: it just wraps a device context handle
with a Graphics object.

I don't know how CreateGraphics works, but presumably it calls either GetDC
or GetWindowDC. Okay, I admit it could call CreateDC but that seems less
likely for screen drawing. Whether it calls GetDC or GetWindowDC probably
depends on whether the Control and/or Form class always uses an "own DC" for
the window.

So yes, it does kind of look like the same DC may be getting handed back and
forth to multiple Graphics objects, and yes it does seem to me that would be
wasteful.

Ain't legacy code great? :)

Pete
 
B

Bob Powell [MVP]

CreateGraphics will get you a Graphics object based on the client area of
the window.

FromHdc using the handle from GetWindowDC will return a graphics object
based on the whole window including the non-client area. This DC is normally
only used by the system unless you want to do clever stuff like override the
non client drawing.

You should really use niether. See the GDI+ FAQ for how and when to get a
Graphics object.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
 
P

pigeonrandle

Peter, Bob,

Thankyou both for your replies.

Bob,
If i might ask, why do you think the author of the above code used this
method? I am about to read the GDI+ FAQ, but are there some appropriate
questions i should be asking that would hasten my successful completion
of a similar control?
CreateGraphics will get you a Graphics object based on the client area of
the window.

FromHdc using the handle from GetWindowDC will return a graphics object
based on the whole window including the non-client area. This DC is normally
only used by the system unless you want to do clever stuff like override the
non client drawing.

You should really use niether. See the GDI+ FAQ for how and when to get a
Graphics object.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.



pigeonrandle said:
Hi,
Please pity me, i am on a dial-up connection for the first time in 5
years :( !

Does anyone know how the resulting Graphics objects differ ...? What i
really mean is can someone explain it to me please?

A)
[DllImport("user32.dll")] protected static extern IntPtr GetWindowDC
(IntPtr hWnd );
hDC = GetWindowDC(this.Handle);
g_dc = Graphics.FromHdc(hDC);

B)
Graphics g = this.CreateGraphics();

You have my thanks and full attention,
James Randle.
 
P

pigeonrandle

Bob (again!),
Hi. I've had a read through the GDI+ FAQ. Am i correct in the following
suggestions?
1) do WM_PAINT control painting in overridden OnPaint() event (after
base.OnPaint(e))
2) WM_NC_PAINT still needs to be handled in WndProc as it uses 'fancy
stuff'

I just read that the combobox has no 'non-client' area so can i discard
WM_NC_PAINT all together?

Thanks and thanks again,
James Randle.

CreateGraphics will get you a Graphics object based on the client area of
the window.

FromHdc using the handle from GetWindowDC will return a graphics object
based on the whole window including the non-client area. This DC is normally
only used by the system unless you want to do clever stuff like override the
non client drawing.

You should really use niether. See the GDI+ FAQ for how and when to get a
Graphics object.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.



pigeonrandle said:
Hi,
Please pity me, i am on a dial-up connection for the first time in 5
years :( !

Does anyone know how the resulting Graphics objects differ ...? What i
really mean is can someone explain it to me please?

A)
[DllImport("user32.dll")] protected static extern IntPtr GetWindowDC
(IntPtr hWnd );
hDC = GetWindowDC(this.Handle);
g_dc = Graphics.FromHdc(hDC);

B)
Graphics g = this.CreateGraphics();

You have my thanks and full attention,
James Randle.
 
P

pigeonrandle

I just tried to call the drawing functions from an overriden OnPaint()
and the combo has reverted to its 3d appearance. Does this mean i have
to break the rules, or am i doing something wrong?

Arrggghhh!
James Randle.
Bob (again!),
Hi. I've had a read through the GDI+ FAQ. Am i correct in the following
suggestions?
1) do WM_PAINT control painting in overridden OnPaint() event (after
base.OnPaint(e))
2) WM_NC_PAINT still needs to be handled in WndProc as it uses 'fancy
stuff'

I just read that the combobox has no 'non-client' area so can i discard
WM_NC_PAINT all together?

Thanks and thanks again,
James Randle.

CreateGraphics will get you a Graphics object based on the client area of
the window.

FromHdc using the handle from GetWindowDC will return a graphics object
based on the whole window including the non-client area. This DC is normally
only used by the system unless you want to do clever stuff like override the
non client drawing.

You should really use niether. See the GDI+ FAQ for how and when to get a
Graphics object.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.



pigeonrandle said:
Hi,
Please pity me, i am on a dial-up connection for the first time in 5
years :( !

Does anyone know how the resulting Graphics objects differ ...? What i
really mean is can someone explain it to me please?

A)
[DllImport("user32.dll")] protected static extern IntPtr GetWindowDC
(IntPtr hWnd );
hDC = GetWindowDC(this.Handle);
g_dc = Graphics.FromHdc(hDC);

B)
Graphics g = this.CreateGraphics();

You have my thanks and full attention,
James Randle.
 
P

pigeonrandle

Problem solved!

placing

this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint,
true);

in the constructor (UserPaint being the important bit) has restored the
missing paint event, and the draw flat button code can be called from
an overriden onpaint().

Thanks to anyone half writing a reply as i write mine, and again thanks
to Bob for pointing me in the right direction.

James Randle.
I just tried to call the drawing functions from an overriden OnPaint()
and the combo has reverted to its 3d appearance. Does this mean i have
to break the rules, or am i doing something wrong?

Arrggghhh!
James Randle.
Bob (again!),
Hi. I've had a read through the GDI+ FAQ. Am i correct in the following
suggestions?
1) do WM_PAINT control painting in overridden OnPaint() event (after
base.OnPaint(e))
2) WM_NC_PAINT still needs to be handled in WndProc as it uses 'fancy
stuff'

I just read that the combobox has no 'non-client' area so can i discard
WM_NC_PAINT all together?

Thanks and thanks again,
James Randle.

CreateGraphics will get you a Graphics object based on the client area of
the window.

FromHdc using the handle from GetWindowDC will return a graphics object
based on the whole window including the non-client area. This DC is normally
only used by the system unless you want to do clever stuff like override the
non client drawing.

You should really use niether. See the GDI+ FAQ for how and when to get a
Graphics object.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.



Hi,
Please pity me, i am on a dial-up connection for the first time in 5
years :( !

Does anyone know how the resulting Graphics objects differ ...? What i
really mean is can someone explain it to me please?

A)
[DllImport("user32.dll")] protected static extern IntPtr GetWindowDC
(IntPtr hWnd );
hDC = GetWindowDC(this.Handle);
g_dc = Graphics.FromHdc(hDC);

B)
Graphics g = this.CreateGraphics();

You have my thanks and full attention,
James Randle.
 
P

pigeonrandle

Err...Problem created.

Now the drop down is a solid black color, and none of the text is
displayed. I now see why the contrived method was used in the first
place ...
it allowed user painting of some of the control whilst letting the text
drawing continue as expected.

Is there a way to accomplish this properly? To be honest, i dont want
to have to write loads of drawing code (that is already there) just
because i want to draw a flat button and flat border which wouldnt
obscure the text anyway.

Any comments?
James Randle

Problem solved!

placing

this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint,
true);

in the constructor (UserPaint being the important bit) has restored the
missing paint event, and the draw flat button code can be called from
an overriden onpaint().

Thanks to anyone half writing a reply as i write mine, and again thanks
to Bob for pointing me in the right direction.

James Randle.
I just tried to call the drawing functions from an overriden OnPaint()
and the combo has reverted to its 3d appearance. Does this mean i have
to break the rules, or am i doing something wrong?

Arrggghhh!
James Randle.
Bob (again!),
Hi. I've had a read through the GDI+ FAQ. Am i correct in the following
suggestions?
1) do WM_PAINT control painting in overridden OnPaint() event (after
base.OnPaint(e))
2) WM_NC_PAINT still needs to be handled in WndProc as it uses 'fancy
stuff'

I just read that the combobox has no 'non-client' area so can i discard
WM_NC_PAINT all together?

Thanks and thanks again,
James Randle.


Bob Powell [MVP] wrote:
CreateGraphics will get you a Graphics object based on the client area of
the window.

FromHdc using the handle from GetWindowDC will return a graphics object
based on the whole window including the non-client area. This DC is normally
only used by the system unless you want to do clever stuff like override the
non client drawing.

You should really use niether. See the GDI+ FAQ for how and when to get a
Graphics object.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.



Hi,
Please pity me, i am on a dial-up connection for the first time in 5
years :( !

Does anyone know how the resulting Graphics objects differ ...? What i
really mean is can someone explain it to me please?

A)
[DllImport("user32.dll")] protected static extern IntPtr GetWindowDC
(IntPtr hWnd );
hDC = GetWindowDC(this.Handle);
g_dc = Graphics.FromHdc(hDC);

B)
Graphics g = this.CreateGraphics();

You have my thanks and full attention,
James Randle.
 

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