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.
Draw on multiple Screen without a form
The solution linked describes using a Display Context Handle (hdc) to get a drawing area for GDI+. An hdc is typically a windows's drawing area or the primary display. As keith mentioned in the comments, you will need to get an HDC for the 2nd display and draw to that as well.
https://msdn.microsoft.com/en-us/library/dd144947%28v=vs.85%29.aspx
Here is another question that is attempting a similar result in C++. The concepts should be pretty much the same.
How to draw over second monitor with GDIPLUS
Drawing on the Desktop using C#
probably windows would have refreshed your screen and that's why you don't see the rectangle in your screen.
The below suggestion may not be a perfect solution, but it may help you to get your code to work.
Add a paint handler to your form as @vivek verma suggested and move your code inside this paint handler
private void Form1_Paint(object sender, PaintEventArgs e)
{
IntPtr desktop = GetDC(IntPtr.Zero);
using (Graphics g = Graphics.FromHdc(desktop))
{
g.FillRectangle(Brushes.Red, 0, 0, 100, 100);
}
ReleaseDC(IntPtr.Zero, desktop);
}
This will make the rectangle being redrawn in your screen when your form will be repainted. But still remember that your drawing on the screen will be gone when the screen is refreshed by windows.
EDIT:
There is also a good post here draw on screen without form that suggests an alternate solution of using borderless form.
How to draw to a pixel on the screen without a window in F#?
Here is a minimal example that uses the GetDC
and SetPixel
functions to do exactly the same thing as the Python example in the referenced question:
open System
open System.Runtime.InteropServices
[<DllImport("user32.dll",EntryPoint="GetDC")>]
extern IntPtr GetDC(IntPtr ptr)
[<DllImport("gdi32.dll")>]
extern uint32 SetPixel(IntPtr hdc, int X, int Y, uint32 crColor);
let dc = GetDC(IntPtr.Zero)
for i in 0 .. 255 do
let r, g, b = 255, i, 255
let clr = (r <<< 16) ||| (g <<< 8) ||| b
SetPixel(dc, i, 0, uint32 clr) |> ignore
That said, I cannot imagine a scenario where this would be a good thing to do. If you are creating a sensible windows application, you will most certainly want to draw anything in the window belonging to your app.
Drawing C# graphics without using Windows Forms
Right, the way i've done it is with a windows form, but make the background transparent, and then get rid of all the borders...
Thanks for the replys anyway..
J
How can i capture the screen without the Form?
Um.. Hide the form?
this.Visible = false;
and THEN run the screenshot method.
Like this:
protected Bitmap TakeScreenshot(bool cursor)
{
Bitmap bitmap;
this.Visible = false;
bitmap = CaptureScreen(cursor);
this.Visible = true;
return bitmap;
}
and use it in your code the way you wanted:
private void timer1_Tick(object sender, EventArgs e)
{
using (bitmap = (Bitmap)ScreenCapture.TakeScreenshot(true))
{
ffmp.PushFrame(bitmap);
}
}
How do I draw graphics in C# without a form
EDIT - based on CuddleBunny's comment, I have created a class that will basically "draw graphics on the screen."
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
class test : Form
{
public test() : base()
{
this.TopMost = true;
this.DoubleBuffered = true;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
this.BackColor = Color.Purple;
this.TransparencyKey = Color.Purple;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Black, 0, 0, 200, 200);
this.Invalidate(); //cause repaint
}
public static void Main(String[] args)
{
Application.Run(new test());
}
}
}
Hope it helps.
old faulty answer
You can get the hwnd of another window and draw on that. I'm not sure how to draw on the entire screen though, I've always wondered that myself.
A simple example :
Process p = Process.GetProcessById(0); //id of the process or some other method that can get the desired process
using (Graphics g = Graphics.FromHwnd(p.MainWindowHandle))
{
g.DrawRectangle(Pens.Black, 0, 0, 100, 100);
}
How to draw an empty rectangle on screen with Python
In order to refresh the old drawn area, you need to either call win32gui.UpdateWindow or something similar to update your specific window, but since you're not technically drawing on a surface, but the entire monitor. You'll need to invalidate the entire region of your monitor in order to tell windows to re-draw anything on it (or so I understand it).
And to overcome the slowness, instead of using for loops to create the boundary which will take X cycles to iterate over before completing the rectangle, you could use win32ui.Rectangle
to draw it in one go:
import win32gui, win32ui
from win32api import GetSystemMetrics
dc = win32gui.GetDC(0)
dcObj = win32ui.CreateDCFromHandle(dc)
hwnd = win32gui.WindowFromPoint((0,0))
monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
while True:
m = win32gui.GetCursorPos()
dcObj.Rectangle((m[0], m[1], m[0]+30, m[1]+30))
win32gui.InvalidateRect(hwnd, monitor, True) # Refresh the entire monitor
Further optimizations could be done here, like not update the entire monitor, only the parts where you've drawn on and so on. But this is the basic concept :)
And to create a rectangle without the infill, you could swap Rectangle
for DrawFocusRect
for instance. Or for more control, even use win32gui.PatBlt
And apparently setPixel
is the fastest, so here's my final example with color and speed, altho it's not perfect as the RedrawWindow
doesn't force a redraw, it simply asks windows to do it, then it's up to windows to honor it or not. InvalidateRect
is a bit nicer on performance as it asks the event handler to clear the rect when there's free time to do so. But I haven't found a way more agressive than RedrawWindow
, even tho that is still quite gentle. An example to this is, hide the desktop icons and the below code won't work.
import win32gui, win32ui, win32api, win32con
from win32api import GetSystemMetrics
dc = win32gui.GetDC(0)
dcObj = win32ui.CreateDCFromHandle(dc)
hwnd = win32gui.WindowFromPoint((0,0))
monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
red = win32api.RGB(255, 0, 0) # Red
past_coordinates = monitor
while True:
m = win32gui.GetCursorPos()
rect = win32gui.CreateRoundRectRgn(*past_coordinates, 2 , 2)
win32gui.RedrawWindow(hwnd, past_coordinates, rect, win32con.RDW_INVALIDATE)
for x in range(10):
win32gui.SetPixel(dc, m[0]+x, m[1], red)
win32gui.SetPixel(dc, m[0]+x, m[1]+10, red)
for y in range(10):
win32gui.SetPixel(dc, m[0], m[1]+y, red)
win32gui.SetPixel(dc, m[0]+10, m[1]+y, red)
past_coordinates = (m[0]-20, m[1]-20, m[0]+20, m[1]+20)
Issues with positions and resolution? Be aware that high DPI systems tend to cause a bunch of issues. And I haven't found many ways around this other than going over to a OpenGL solution or using frameworks such as wxPython or OpenCV other than this post: Marking Your Python Program as High DPI Aware Seamlessly Windows
Or changing the Windows display scale to 100%
:
This causes the positioning issue to go away, perhaps take this to account by querying the OS for the scale and compensate.
The only reference I could find on the "clearing the old drawings" was this post: win32 content changed but doesn't show update unless window is moved tagged c++
win winapi
. Hopefully this saves some people from searching before finding a good example.
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);
}
}
Related Topics
Play Multiple Sounds Using Soundplayer
C# Random Numbers Aren't Being "Random"
How to Bind Datatable to Datagridview in C#
Is There a C# Generic Constraint for "Real Number" Types
Jquery Ajax Call to an ASP.NET Webmethod
How to Write the Escape Char '\' to Code
Modifying a JSON File Using System.Text.JSON
Run .Exe Executable File in Azure Function
The Property 'Id' Is Part of the Object's Key Information and Cannot Be Modified
What Could Be Causing a System.Typeloadexception
"Interface Not Implemented" When Returning Derived Type
Get Line Number for Xelement Here
Linq Query Built in Foreach Loop Always Takes Parameter Value from Last Iteration
Ef4 Code First: How to Add a Relationship Without Adding a Navigation Property