How to effectively draw on desktop in C#?
When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.
There are a couple of options, depending upon what you want to achieve:
- create a borderless, possibly
non-rectangular window. (Use
SetWindowRgn to make a window
non-rectangular.) You can make this a child of the desktop window. - subclass the desktop window. This is not straightforward, and involves
injecting a DLL into the
Explorer.exe process.
How to draw directly on the Windows desktop, C#?
Try the following:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
class Program {
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
static extern int ReleaseDC(IntPtr hwnd, IntPtr dc);
static void Main(string[] args) {
IntPtr desktop = GetDC(IntPtr.Zero);
using (Graphics g = Graphics.FromHdc(desktop)) {
g.FillRectangle(Brushes.Red, 0, 0, 100, 100);
}
ReleaseDC(IntPtr.Zero, desktop);
}
}
Draw on the screen without a form
Method 1: Call the Windows API
You need System.Drawing
and System.Runtime.InteropServices
. You may need to add project references to them.
using System.Runtime.InteropServices;
using System.Drawing;
Add the methods to your class with P/Invoke
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
public static extern void ReleaseDC(IntPtr hwnd, IntPtr dc);
Get a Graphics
object for the entire screen and draw a rectangle with it:
IntPtr desktopPtr = GetDC(IntPtr.Zero);
Graphics g = Graphics.FromHdc(desktopPtr);
SolidBrush b = new SolidBrush(Color.White);
g.FillRectangle(b, new Rectangle(0, 0, 1920, 1080));
g.Dispose();
ReleaseDC(IntPtr.Zero, desktopPtr);
The problem with this method is that if the screen refreshes at all, the rectangle will be overwritten, making it useless for most practical applications.
Method 2: Create a borderless form
As before, you need a project reference. This time to System.Windows.Forms
. You'll also need System.Drawing
again:
using System.Drawing;
using System.Windows.Forms;
Make the new form, remove its borders, fill the screen with it, and put it on top of the taskbar:
Form f = new Form();
f.BackColor = Color.White;
f.FormBorderStyle = FormBorderStyle.None;
f.Bounds = Screen.PrimaryScreen.Bounds;
f.TopMost = true;
Application.EnableVisualStyles();
Application.Run(f);
A possible issue with this is that the user can just alt+tab away from the window. If you want to do any more complicated graphics, you'll need to write some drawing code like this. To make the form background transparent, set its TransparentKey
to the same as its Backolor
.
I've just tested both of these in .NET 4.5 and Windows 7, so it may be different for earlier versions. More information here and here.
drawing a string on the screen C#
To draw a string outside of your window, you'll have to CREATE a new window, set it's mask to some color (say magenta) and then draw text onto it - you can use simple label here.
Set your window border style to None, and there you go.
In other words, there is no way of displaying 'free text' without window attached.
For masking color, use 'transparency color' or similar property (I will look up into it later - have no VS at hand)
How to clean up after myself when drawing directly to the screen
InvalidateRect(NULL, NULL, TRUE)
should invalidate and redraw all windows. Though it will be expensive operation.
An alternative approach would be to remember where you've drawn and try to enumerate any windows in that rectangle and invalidate only them.
You can also try creating a transparent window over the area you've painted when you want to invalidate the windows there. Note that you want to do alpha-blended layered window, not an WS_TRANSPARENT window. If you do it with 99% transparency, the DWM should repaint all the windows below it once it's destroyed. Of course, this could lead to barely perceptible flicker over the target region due to the alphablending of the window before you destroy it.
How to draw on a Window in WPF (best practice)?
Typically, you "draw" in WPF in a completely different manner.
In Windows Forms/GDI, the graphics API is an immediate mode graphics API. Each time the window is refreshed/invalidated, you explicitly draw the contents using Graphics.
In WPF, however, things work differently. You rarely ever directly draw - instead, it's a retained mode graphics API. You tell WPF where you want the objects, and it takes care of the drawing for you.
The best way to think of it is, in Windows Forms, you'd say "Draw a line from X1 to Y1. Then draw a line from X2 to Y2. Then ...". And you repeat this every time you need to "redraw" since the screen is invalidated.
In WPF, instead, you say "I want a line from X1 to Y1. I want a line from X2 to Y2." WPF then decides when and how to draw it for you.
This is done by placing the shapes on a Canvas, and then letting WPF do all of the hard work.
Related Topics
Internal VS. Private Access Modifiers
How to Make My Own Event in C#
How to Align Text in Columns Using Console.Writeline
Determine Assembly Version During a Post-Build Event
Sending Outlook Meeting Requests Without Outlook
Why Would I Bother to Use Task.Configureawait(Continueoncapturedcontext: False);
Can You Explain Liskov Substitution Principle with a Good C# Example
How to Store Data Locally in .Net (C#)
Spawn Multiple Threads for Work Then Wait Until All Finished
How to Convert Securestring to System.String
What Is the Efficiency and Performance of Linq and Lambda Expression in .Net
When Is Finally Run If You Throw an Exception from the Catch Block
Camera.Main Is Null When Performing Raycast
How to Route Images Using ASP.NET MVC Routing
How to Ignore the Utf-8 Byte Order Marker in String Comparisons