Drawing simple shapes and text

Draw GDI+ flicker-free with managed double-buffering

Overriding what a control draws

Safe resource allocation

Visual Studio (Any version of the IDE should work)

.NET Framework I'm using 4.7.2 in this tutorial, to avoid problems please use this version

I'm assuming that you have some programming experience as I won't go over basic concepts like setting up the project

Starting out

OnPaint()

CTRL + PERIOD or .

C#: protected override void OnPaint ( PaintEventArgs e ) { base.OnPaint(e); }

base.OnPaint(e)

PaintEventArgs e

C#: protected override void OnPaint ( PaintEventArgs e ) { base.OnPaint(e); using (SolidBrush b = new SolidBrush(Color.DeepSkyBlue)) { e.Graphics.FillRectangle(b, new Rectangle(0,0,Width/2,Height)); } e.Graphics.Dispose(); }

using (SolidBrush b = new SolidBrush(Color.DeepSkyBlue))

using (Type obj = new Type()) { }

Color.FromARGB(A, R, G, B)

e.Graphics.FillRectangle(b, new Rectangle(0,0,Width/2,Height))

FillRectangle(Brush, Rectangle)

e.Graphics

e.Graphics

Graphics.Dispose()

Let's draw some Text!

OnPaint()

C#: protected override void OnPaint ( PaintEventArgs e ) { base.OnPaint(e); using (SolidBrush b = new SolidBrush(Color.DeepSkyBlue)) { e.Graphics.FillRectangle(b, new Rectangle(0,0,Width/2,Height)); } StringFormat stringFormat = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near }; using (SolidBrush f = new SolidBrush(ForeColor)) { e.Graphics.DrawString("Hello World!", Font, f, Bounds, stringFormat); } stringFormat.Dispose(); e.Dispose(); }

stringFormat

LineAlignment

Alignment

using (Type obj = new Type()) { }

e.Graphics.DrawString()

Text, Font, Bounds and StringFormat

StringFormat

Dispose()

Graphics.DrawString()

C#: using (SolidBrush f = new SolidBrush(ForeColor)) { Size strSize = TextRenderer.MeasureText("Hello World!", Font); Rectangle controlBounds = new Rectangle(Location, new Size(Size.Width - strSize.Width / 2, Size.Height - strSize.Height / 2)); e.Graphics.DrawString("Hello World!", Font, f, controlBounds, stringFormat); }

strSize

TextRenderer.MeasureText(Text, Font)

controlBounds

strSize

StringAlignment.Center

Drawing Border - Are you edgy ?

C#: using (Pen p = new Pen(Color.DimGray)) { e.Graphics.DrawRectangle(p, new Rectangle(0, 0, Width-1, Height-1)); }

Pen

SolidBrush

e.Graphics.DrawRectangle(Pen, Bounds)

e.Graphics.FillRectangle(Brush, Bounds)

C#: using (Pen p = new Pen(Color.DimGray)) { e.Graphics.DrawLines(p, new Point[] { new Point(Width / 2, 0), new Point(Width / 2, Height) }); }

e.Graphics.DrawLines

Index

Point

Point

Center-Top

Center-Bottom

Flicker-free Drawing - Remove the Epilepsy Nuisance

DoubleBuffered = true;

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

DoubleBuffered = true;

Manually Managed Buffered Graphics C# - Pastebin.com Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.

DBGraphics.cs

using System.Drawing;

protected DBGraphics _memGraphics;

private int leftOffset = 0;

C#: public GDI_Test () { InitializeComponent(); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); _memGraphics = new DBGraphics(); _memGraphics.CreateDoubleBuffer(this.CreateGraphics(), this.ClientRectangle.Width, this.ClientRectangle.Height); Timer tmr = new Timer() { Enabled = true, Interval = 10 }; tmr.Tick += ( sender, args ) => { if (leftOffset >= Width / 2) { leftOffset = 0; } leftOffset += 5; Invalidate(); }; }

OnPaint()

e.Dispose()

if (_memGraphics.CanDoubleBuffer())

_memGraphics.g.Clear(BackColor);

e.Graphics

_memGraphics.g

_memGraphics.Render(e.Graphics);

stringFormat.Dispose();

Spoiler: Code C#: protected override void OnPaint ( PaintEventArgs e ) { if (_memGraphics.CanDoubleBuffer()) { _memGraphics.g.Clear(BackColor); using (SolidBrush b = new SolidBrush(Color.DeepSkyBlue)) { _memGraphics.g.FillRectangle(b, new Rectangle(leftOffset, 0, Width / 2, Height)); } using (Pen p = new Pen(Color.DimGray)) { _memGraphics.g.DrawLines(p, new Point[] { new Point(Width / 2, 0), new Point(Width / 2, Height) }); } StringFormat stringFormat = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }; using (SolidBrush f = new SolidBrush(ForeColor)) { Size strSize = TextRenderer.MeasureText("Hello World!", Font); Rectangle controlBounds = new Rectangle(Location, new Size(Size.Width - strSize.Width / 2, Size.Height - strSize.Height / 2)); _memGraphics.g.DrawString("Hello World!", Font, f, controlBounds, stringFormat); } using (Pen p = new Pen(Color.DimGray)) { _memGraphics.g.DrawRectangle(p, new Rectangle(0, 0, Width - 1, Height - 1)); } _memGraphics.Render(e.Graphics); stringFormat.Dispose(); } e.Dispose(); } protected override void OnPaintBackground ( PaintEventArgs e ) { }

OnPaintBackground()

Notes

We're using Invalidate() in the Timer.Tick() function this forces the Control to re-paint itself. This can be very resource intensive if called frequently. I refrained from using var to make it easier to understand.



GDI_Test.cs - Pastebin.com Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.

So you want to learn a dying, bloated and slow API by Microsoft ? Let's Go!In all seriousness I love GDI+ and even WinForms too, I started out making my first UI-only based games in WinForms using the ancient Visual Basic 6 language.I'm biased, there's no doubt about it, it's a dying (some would say dead) API and i would agree to an extent. However I don't think it's obsolete as there is still some companies that use the platform to this day.Let's start of by telling you what I'll be covering in this tutorial first so you know what you're in for!RequirementsStart off by creating a new C# WinForms .NET Framework project then add a new User Control to the project name it whatever you want I named mine "GDI_Test". And then go into Code View of the User Control by double-clicking the control inside the designer.We're now override the control'sfunction so we can start drawing custom graphics to it. You can do this my pressingif your shortcuts are the same as mine. Or you can copy paste the code below.Let's explain a few things here:Refers to the OnPaint(e) function of the Control that this User Control is inheriting from. If you want to draw everything yourself you can safely remove this line. We will keep it for the tutorial's sake.Is the paint arguments of this function. It contains the Graphics Context that we will be using to draw.Now we will draw a rectangle over the User Control.This draws a Filled Rectangle with half of the Width of the control.This is important to remember: Graphics objects won't get disposed (deleted from memory) after the function is completed. If you fail to dispose of a Graphics Object, the garbage collector has to clear the object which is resource very intensive. Withi dispose the object automagically when code block is completed so you don't have to manually dispose them yourself. It's also here i define what Brush to use. You can use any color you want includingIs the "Draw call" it draws ausingwith the specified Brush(Color) and Rectangle(Left, Top, Width, Height).After I'm done Drawing what i want, i dispose of theobject withTo test out the drawing simply Build your project (F7), go back to your Form (Form1) then look for the User Control in the toolbox it should be at the top and then add it to your Form.You might be asking why you can't see the entire User Control, but you can! The User Control Inherits the Parent's BackColor when you haven't specified the BackColor of the User Control. To change this Stop Debugging and click the User Control within the Form Designer and change the BackColor to something else.Go back to the User Controlsfunction and change to this:I added a new variable calledthis isn't required to draw text but it aligns the text within the draw bounds. In this case(Vertical Alignment) is centered and(Horizontal Alignment) is near (Left).Again i usehere to dispose of the Brush after it's done.This draws the text usingin this case. This function has a lot of overloads you can use you don't needif you don't want it.When I'm done iof the StringFormat object.I should also note that StringFormat is probably something you should define in the Class itself instead of initiating the Object every time you Paint the User Control. I did it there because of simplicity.If you run this now you should be seeing something off. The text isn't perfectly centered. This is due todoesn't account for the Bounds of the drawn text. This can be fixed like so:Is the sizemeasured from the Text and Font specified.Subtracts the measured Size fromdivided by 2.(I changed Alignment totry and do the same!)To Draw a Border simply add this underneath the other Drawing functions:This is almost the same as when we drew the Filled Rectangle only this draws the outlines of a Rectangle. We use ainstead ofbecause this is single pixel drawing. We useinstead ofPlease note that i subtract a single pixel from the Width and Height. This will draw the Border INSIDE the bounds of the Rectangle and not outside.I will also add a accent line in the center like so:Draws connected lines using an array of Locations (Points) with the specified Pen Color.It draws the points in sequence or one after another based on theof the Point and draws a line from the previousto the next. In this case it draws the firstand the secondof the User Control.When you draw graphics using GDI+ you might notice some flickering especially if you're rendering animations. Now GDI+ has a solution called DoubleBuffering.Basically what it does is when you call say DrawFillRectangle is draws to a canvas in memory instead of the drawing surface on the screen. This allows you only have graphics operation performed instead of multiple ones.O-K-I listen up, this is important: It's different how you enable DoubleBuffering depending if it's a Standard Windows Forms control or if you have written all the rendering code.Usually WinForms controls are DoubleBuffered by default but there can be some yanky code running that disables it.So to enable it simply set the DoubleBuffered property to true:BUT, if you're handling all the rendering code yourself set it this way instead:To show this I've made a very simple animation that moves the left rectangle to the right by 5px every 10ms using a timer:As you can see the text and the colors flicker.If you try to setthe program will crash because you are disposing the Graphics Object in the OnPaint() function.And even if you don't dispose the Graphics Object it will still flicker the same.Solution ?Enter Manually Managed Buffered Graphics!We will steal some code that NT Almond wrote back in 2003 because I'm lazy and don't want to write my own.Put that inside a class calledand remember to addto the top of the class!Add a declaration of the DBGraphics object in the class as protected:andfor the animation.Then initialize the object in the Constructor of the User Control and add the animation timer like so:Inside thefunction we are going to modify some things.First, wrap everything exceptinside an if statement with the condition:Addas the first line inside the if statement.Change every occurence ofwithAddBEFOREwithin the if statement.Remove base.OnPaint() from the function.Override the OnPaintBackground() event and remove base.OnPaintBackground() from it so to make it an empty function.Or you can just copy & paste the here:If all it good with the C# gods you should have something like this:Nice huh ? And that's 17 years old code.Now to explain what DBGraphics does.It draws everything to a Bitmap Image and then renders it when everything is finished drawing. This is slower than un-buffered rendering so you might want to avoid this if it hinders performance too much.We're also overridingto handle all the rendering ourselves.GDI_Test class: