Drawing on top of a form (including drawing over it's controls)

Drawing on top of a form (including drawing over it's controls)

Post by James » Sun, 27 Sep 2009 19:43:28


Hi All,
I'm developing an application using VS 2008 and it contains a
Windows.Form which contains the usual Labels TextBoxes+ Buttons.

For a debug function (which will probably make it into production!) I'd
like to draw some text etc. on top of the form and it's controls when I
hold down a certain key. (It will be extra labels and arrows etc.
drawn using the standard .NET drawing primitives available on the
Graphics object)

I've found I can grab a Graphics object for the form and draw on that
but that only lets me draw on the form background and not on the
controls, adding code the the Form's paint event also had the same
results. I can also get a Graphics object for each control and draw on
those one by one, but that seems an awful lot of work and not very elegant.

After some Googling I found something that uses the BitBlt windows API
function to generate an Image containing a clone of the current form
image and drawing everything on that, but it also doesn't seem very
elegant. I also thought I could draw onto a full screen sized Label
that I had set to transparent, but that doesn't work properly either.

Is there a better way to do what I want to accomplish preferably using
the .NET Framework?

Any suggestions welcome.

Cheers,

James.
 
 
 

Drawing on top of a form (including drawing over it's controls)

Post by Mick Doher » Mon, 28 Sep 2009 06:00:27

i James,

You can always create a control which sets the WS_EX_TRANSPARENT style in
CreateParams (a quick search should find plenty of examples).

However, although it uses some Interop, my suggestion would be to use a
layered window. This option will allow you to paint anywhere on the screen,
not just within the bounds of the form.

Here's a quick c# example of such a form. I have also enabled mouse clicks
to pass straight through:

--- 8< ---------------------------------------------------------------

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

namespace Dotnetrix.Samples
{
public class DebugWindow : Form
{
public DebugWindow(Form owner)
: base()
{
this.SetStyle(ControlStyles.Selectable, false);
this.WindowState = FormWindowState.Maximized;
this.FormBorderStyle = FormBorderStyle.None;
this.TopMost = true;
this.ShowInTaskbar = false;
this.Owner = owner;
}

protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
if (!this.DesignMode)
{
cp.ExStyle |= NativeMethods.WS_EX_LAYERED;
}
return cp;
}
}

protected override void WndProc(ref Message m)
{

switch(m.Msg)
{
case 0x84: // WM_NCHITTEST
m.Result = new IntPtr(-1); // HTTRANSPARENT
return;

}

base.WndProc(ref m);

if (m.Msg == 0x86) // WM_NCACTIVATE
{
if (m.HWnd == this.Handle && m.WParam.ToInt32() != 0)
{
Message msg = Message.Create(Owner.Handle, 0x86,
(IntPtr)(1), IntPtr.Zero);
base.WndProc(ref msg);
}
}

}

protected override void CreateHandle()
{
base.CreateHandle();
//Disable VisualStyles as we're doing all painting ourselves.
if (OSFeature.Feature.IsPresent(OSFeature.Themes))
NativeMethods.SetWindowTheme(this.Handle, null, "");
}

protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
this.UpdateWindow();
}

public void UpdateWindow()
{
if (this.IsDisposed || this.Width <= 0 || this.Height <= 0 ||
!this.Visible)
return;

using (Bitmap backBuffer = new Bitmap(this.Width, this.Height,
PixelFormat.Format32bppPArgb))
{
using (Graphics gr = Graphics.FromImage(backBuffer))
{
gr.SmoothingMode = SmoothingMode.AntiAlias;

Point pt = this.PointToScreen(Point.Empty);
pt.Offset(-this.Left, -this.Top);
Rectangle rc =
this.RectangleToScreen(this.ClientRectangle);
rc.Offset(-this.Left, -this.Top);

if (this.ClientSize.Width > 0 && this.ClientSize.Height
> 0)
{
//Paint the Window