Drawing C# graphics without using Windows Forms - c#

Could someone provide an example for drawing graphics without using Windows Forms? I have an app that doesn't have a console window or Windows form, but i need to draw some basic graphics (lines and rectangles etc.)
Hope that makes sense.

This should give you a good start:
[TestFixture]
public class DesktopDrawingTests {
private const int DCX_WINDOW = 0x00000001;
private const int DCX_CACHE = 0x00000002;
private const int DCX_LOCKWINDOWUPDATE = 0x00000400;
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetDCEx(IntPtr hwnd, IntPtr hrgn, uint flags);
[Test]
public void TestDrawingOnDesktop() {
IntPtr hdc = GetDCEx(GetDesktopWindow(),
IntPtr.Zero,
DCX_WINDOW | DCX_CACHE | DCX_LOCKWINDOWUPDATE);
using (Graphics g = Graphics.FromHdc(hdc)) {
g.FillEllipse(Brushes.Red, 0, 0, 400, 400);
}
}
}

Something like this?
using System.Drawing;
Bitmap bmp = new Bitmap(200, 100);
Graphics g = Graphics.FromImage(bmp);
g.DrawLine(Pens.Black, 10, 10, 180, 80);

The question is a little unfocused. Specifically - where do you want to draw the lines and rectangles? Generally speaking, you need a drawing surface, usually provided by a windows form.
Where does the need to avoid windows forms come from?
Are you using another kind of window?
For a windows form you could use code similar to this:
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
e.Graphics.DrawLine(new Pen(Color.DarkGreen), 1,1, 3, 20 );
e.Graphics.DrawRectangle(new Pen(Color.Black), 10, 10, 20, 32 );
}
}
}
You can generally do this with any object that lets you get a handle for a "Graphics" object (like a printer).

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

Related

WPF C# MoveWindow issue under Windows 11

I have an application with at least 2 separate windows.
The secondary window is too far from the PC to use the mouse, so I have a method that temporarily brings that specific window to the current main display, changes are done, then the window is sent back.
This worked well under Windows 10, however under Windows 11, the window seems to disappear and is nowhere to be seen during the initial call. It can however be sent back (from wherever it was hiding) to the secondary monitor.
Here is some code to position the window (normal MoveWindow):
// Position is assigned in the constructor of the second window
public System.Drawing.Rectangle Position { get; set; }
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
MoveW();
}
public void MoveW()
{
WindowInteropHelper wih = new(this);
IntPtr hWnd = wih.Handle;
if (!Position.IsEmpty)
{
_ = MoveWindow(hWnd, Position.Left, Position.Top, Position.Width, Position.Height, false);
}
}
and here is how I am bringing the window to the current display (work perfect Win10):
// Getting the coordinates of the MainWindow
var screen = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(App.Current.MainWindow).Handle);
System.Drawing.Rectangle rect = screen.WorkingArea;
// Simply passing them to second window needing to be moved
if (!rect.IsEmpty)
{
var wih = new WindowInteropHelper(this);
IntPtr hWnd = wih.Handle;
MoveWindow(hWnd, rect.Left, rect.Top, rect.Width, rect.Height, false);
}
Here is the link for MoveWindow
I have created a small GitHub project to illustrate the issue. If you have 2 screens and win10 and 11, get it from here.
Any suggestions?
As far as I noticed, on Windows 11, WindowState.Maximized seems to prevent the window from being shown after its location is changed by MoveWindow function.
So the workround is returing to WindowState.Normal before calling MoveWindow. It would be something like below.
WindowState state = this.WindowState;
try
{
this.WindowState = WindowState.Normal;
MoveWindow(hWnd, rect.Left, rect.Top, rect.Width, rect.Height, false);
}
finally
{
this.WindowState = state;
}

Getting the color of a pixel drawn by DWM in C#

I want to get Screenshots of a possible hidden Window of another application that is using drawing via direct3d or opengl. I tryed a lot of ways to receive this windows content but only got black or transparent pictures. The closest i got was by using a DWM sample here
http://bartdesmet.net/blogs/bart/archive/2006/10/05/4495.aspx
this paints the window onto my c# form but i cant get the pixelcolors. If ill do a form.drawtobitmap the pixels drawn by dwm are missing.
So is their any way to use DWM to recive the capture into a image
or to get the image drawn onto my form?
To answer your question:
You can use GetPixel() Win32 function. But it's overkill in this situation.
Pinvoke GetPixel
MSDN GetPixel
The right way, is to get the device context and bit blit the content.
EDIT:
I've thrown together some code, by using PrintWindow. Seems to work quite well, even with media players. Note that GetWindowRect returns invalid rectangle for minimized Windows. But it's a decent start.
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
internal Rect(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hwnd, out Rect lpRect);
public void DumpWindow(IntPtr hwndSource, string filename)
{
Rect rc;
GetWindowRect(hwndSource, out rc);
var bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
using (Graphics gBmp = Graphics.FromImage(bmp))
{
IntPtr hdcBmp = gBmp.GetHdc();
PrintWindow(hwndSource, hdcBmp, 0);
gBmp.ReleaseHdc(hdcBmp);
}
bmp.Save(filename);
}
Edit2:
And if you add a second button to DWM demo form, insert this:
private void button1_Click(object sender, EventArgs e)
{
var w = (Window)lstWindows.SelectedItem;
DumpWindow(w.Handle, "test.bmp");
Process.Start("test.bmp");
}
It still shows an empty image?

Drawing graphics doesn't work

I have this code where it makes the form always on top, transparent and click through.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace HyperBox
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.TopMost = true; // make the form always on top
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // hidden border
this.WindowState = FormWindowState.Maximized; // maximized
this.MinimizeBox = this.MaximizeBox = false; // not allowed to be minimized
this.MinimumSize = this.MaximumSize = this.Size; // not allowed to be resized
this.TransparencyKey = this.BackColor = Color.Red; // the color key to transparent, choose a color that you don't use
// Set the form click-through
int initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
}
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SetParent(int hWndChild, int hWndNewParent);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(
[MarshalAs(UnmanagedType.LPTStr)] string lpClassName,
[MarshalAs(UnmanagedType.LPTStr)] string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(
IntPtr hWndChild, // handle to window
IntPtr hWndNewParent // new parent window
);
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// draw what you want
e.Graphics.FillRectangle(Brushes.Red, new Rectangle((SystemInformation.WorkingArea.Width / 2) - 4, (SystemInformation.WorkingArea.Height / 2) - 20, 8, 40));
e.Graphics.FillRectangle(Brushes.Red, new Rectangle((SystemInformation.WorkingArea.Width / 2) - 20, (SystemInformation.WorkingArea.Height / 2) - 4, 40, 8));
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
IntPtr hwndf = this.Handle;
IntPtr hwndParent = FindWindow("chrome.exe", null);
SetParent(hwndf, hwndParent);
}
}
}
The problem is when I draw the graphics, it draws nothing. When the coordinates are around 100~ it works. But when it does the method above nothing happens. At all, not even a pixel. Could someone please explain why this is happening and or repost a fixed snippet, thank you. Layne
OnPaint is giving you a graphics object for your form, not the screen. You are filling rectangles based on the working area of the system, not the form. You will need to adjust your rectangle coordinates and position the form where you want your graphics to appear. A rectangle with a location of (0, 0) is the top-left corner of the form's client area. You should also be able to access that rectangle by calling ClientRectangle that is exposed on the base Form class.
Take a look at this question for drawing outside your form: Draw / Paint Outside Form
That should get you started in the right direction if you don't want to paint on your form, but it would probably be easier to reposition and resize your form as needed.
EDIT It would probably be wise to at least add a some sort of border while you debug your issue. This will help you see where the form is positioned and what monitor it is on. You can then check your numbers as you break point in OnPaint to make sure you are creating your rectangles correctly, however, making sure you are painting within the form's client area should fix your issue.

How to draw directly on the Windows desktop, C#?

This question has been asked for other languages, and even for those other languages, I have found their answers lacking in how to exactly do it, cleanly (no messed up screen repaints, etc..).
Is it possible to draw onto the Windows Desktop from C#? I am looking for an example if possible.
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);
}
}
You can try:
Graphics.FromHwnd(IntPtr.Zero)
You can see a real-world code example within https://uiautomationverify.codeplex.com/SourceControl/latest#UIAVerify/Tools/visualuiverify/utils/screenrectangle.cs
This draws a rectangle that will appear on the screen until the user chooses to remove it at an arbitrary position (wont be repainted over). It uses a windows form thats hidden/ appears as a popup.
This is the code behind the UIAVerify.exe tool in the current Windows SDK.
If you want to use the above, copy the following files into your project:
utils\screenboundingrectangle.cs
utils\screenrectangle.cs
win32\*
Might need to update namespaces accordingly + add references to System.Drawing + System.Windows.Forms
Then you can draw a rectangle with the following code:
namespace Something
{
public class Highlighter
{
ScreenBoundingRectangle _rectangle = new ScreenBoundingRectangle();
public void DrawRectangle(Rectangle rect)
{
_rectangle.Color = System.Drawing.Color.Red;
_rectangle.Opacity = 0.8;
_rectangle.Location = rect;
this._rectangle.Visible = true;
}
}
}
and
var rect = Rectangle.FromLTRB(100, 100, 100, 100);
var hi = new Highlighter();
hi.DrawRectangle(rect);

Is there any way to draw a control on some other control in C#?

I would like to draw a control on some other control in it's overriden paint event. By drawing I mean real drawing, not placing the control inside the other control. Is there any nice way to do that?
Try the static methods on the ControlPaint class. Drawn controls may not be skinned like the rest of the GUI, but the effect will be pretty believable. Below is a stripped-down version of some of my code. It is overriding the DrawItem method of an ownerdrawn ListBox to make the list items look like buttons, using ControlPaint.DrawButton method.
There are more goodies on that class for checkboxes, combos, even drag handles.
protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index > -1)
{
String itemText = String.Format("{0}", this.Items.Count > 0 ? this.Items[e.Index] : this.Name);
//Snip
System.Windows.Forms.ControlPaint.DrawButton(e.Graphics, e.Bounds, ButtonState.Normal);
e.Graphics.DrawString(itemText, this.Font, SystemBrushes.ControlText, e.Bounds);
}
}
public delegate void OnPaintDelegate( PaintEventArgs e );
private void panel1_Paint( object sender, PaintEventArgs e ) {
OnPaintDelegate paintDelegate = (OnPaintDelegate)Delegate.CreateDelegate(
typeof( OnPaintDelegate )
, this.button1
, "OnPaint" );
paintDelegate( e );
}
You can add/override OnPaint handler as #TcKs suggested or use BitBlt function:
[DllImport("gdi32.dll")]
private static extern bool BitBlt(
IntPtr hdcDest,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
IntPtr hdcSrc,
int nXSrc,
int nYSrc,
int dwRop
);
private const Int32 SRCCOPY = 0xCC0020;
....
Graphics sourceGraphics = sourceControl.CreateGraphics();
Graphics targetGraphics = targetControl.CreateGraphics();
Size controlSize = sourceControl.Size;
IntPtr sourceDc = sourceGraphics.GetHdc();
IntPtr targerDc = targetGraphics.GetHdc();
BitBlt(targerDc, 0, 0, controlSize.Width, controlSize.Height, sourceDc, 0, 0, SRCCOPY);
sourceGraphics.ReleaseHdc(sourceDc);
targetGraphics.ReleaseHdc(targerDc);
Perhaps what you're after is a "panel" which you can inherit from and then create your own behaviour?
class MyPanel : System.Windows.Forms.Panel
{
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
}
}
Grab e.graphics and you can do pretty much anything you want within the bounds of the control. From memory you can set minimum sizes on your control etc. but you'll need to jump over to windows.forms documentation in MSDN for more specifics (or you could ask another question here ;) ).
Or if your for instance adding functionality, you should inherit from the control your attempting to enhance and override it's paint method?
Perhaps you could elaborate (in your question) what you wish to do this for?
You can do this very easily by using the control's DrawToBitmap method. Here is a snippet that will create a Button and draw it on a same-sized PictureBox:
Button btn = new Button();
btn.Text = "Hey!";
Bitmap bmp = new Bitmap(btn.Width, btn.Height);
btn.DrawToBitmap(bmp, new Rectangle(0, 0, btn.Width, btn.Height));
PictureBox pb = new PictureBox();
pb.Size = btn.Size;
pb.Image = bmp;
To use this approach in another control's Paint event, you would create the Bitmap from the control as above, then draw it on the control's surface like this:
e.Graphics.DrawImage(bmp, 0, 0);
bmp.Dispose();

Categories