Currently I'm using C# datagridview to draw a wafer map which contains >500 rows and >700 columns.
However, there are a few issues:
Slow performance. As i need to adjust the column width, I have to loop through and assign individually.
for (int i = 0; i < this.dgvCompleteMapGrid.Columns.Count; i++)
{
this.dgvCompleteMapGrid.Columns[i].Width = 8;
}
I only need to draw the cell border for cells with value as a wafer map has an almost round shape. I'm using cellpainting event:
if (e.Value != null) {
if (e.Value.ToString() == "")
{
e.AdvancedBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
}
else
{
if (e.Value.ToString() == "1")
{
e.CellStyle.BackColor = Color.Lime;
}
else
{
e.CellStyle.BackColor = Color.Red;
}
//check if the top border set None for neighbor empty value cell
if (e.AdvancedBorderStyle.Top.ToString() == "None")
{
e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.Single;
}
//repeat the same for left right and bottom
}
}
However, it seems like it will draw multiple border duplicate for most of the cells.
Is Datagridview recommended? I tried to draw rectangle on a panel and the performance is even worse.
Here's my Picturebox that allows for pixellated resizing (versus interpolated). E.g. similar to zooming in on a Microsoft Paint picture.
using System.Windows.Forms;
namespace WireWorld
{
public class ZoomablePicturebox : PictureBox
{
public ZoomablePicturebox()
{
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
pe.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
pe.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
base.OnPaint(pe);
}
}
}
To generate the bitmap, I use something like this:
// Pick width and height for your case
Bitmap MyBitmap = new Bitmap(width, height);
using (var g = Graphics.FromImage(retVal))
g.Clear(BackColor); // Pick a background fill color
// Draw your points, whatever your loop needs to be and your point definition is
foreach (MyPointDef point in _Points)
{
MyBitmap.SetPixel(point.X, point.Y, point.Color);
}
Then I put a picture box in a panel on my form. The panel then provides the scrolling. I can set the picture and zoom as follows:
canvasPB.SizeMode = PictureBoxSizeMode.Zoom; // Ensures picture is resized as we resize picturebox
canvasPB.Image = MyBitmap;
canvasPB.Height = canvasPB.Image.Height * Zoom; // Zoom is 1, 2, 3, etc, for 1X, 2X, ...
canvasPB.Width = canvasPB.Image.Width * Zoom;
canvasPB being the name of the Picturebox on my form I'm trying to use as my canvas.
Related
I want to call OnPaint event in my custom control. I have created a class and inherited control to create custom control and want to call OnPaint or paint event when the object is initialized. But when i create a class paint event is not triggered. Se the below code.
internal class CallRectangle : Control
{
public CallRectangle()
{
this.Paint += CalloutRectangle_Paint;
}
void CalloutRectangle_Paint(object sender, PaintEventArgs e)
{
throw new NotImplementedException();
}
protected override void OnPaint(PaintEventArgs e)
{
}
}
// Create object to the custom control and call paint event using constructor
CallRectangle obj = new CallRectangle();
this.Controls.Add(obj);
Any one let me know how to call paint event.
Thanks,
Bharathi.
Calling Invalidate, Update or Refresh will not work for you.
Here is a downloadable Sample from Microsoft with a Step-By-Step Guide on how to Inherit Directly from Control and build the Draw Events, since inheriting from a existing control (Button let's say) is quite easy as it already draws it self.
What you'll see in the above example that you'll need to create multiple drawing events based on the complexity of your Control (like a List box that have items that need to be drawn). Sample code from above link:
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaint"]/*'/>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// draw on memory bitmap
CreateMemoryBitmap();
// TODO: Figure out how to avoid doing this on every paint
// calculate fields required to layout and draw list
RecalcItems(e.Graphics);
Graphics g = Graphics.FromImage(m_bmp);
// Draw the background and raise the PaintBackground event
OnPaintBackground(new ListPaintEventArgs(g));
// draw list
if (m_list != null)
DrawItems(g);
// Draw the frame around the list
Rectangle rc = new Rectangle(0, 0, this.Width - m_scrollBarWidth, this.Height - 1);
g.DrawRectangle(new Pen(Color.Black), rc);
// blit memory bitmap to screen
e.Graphics.DrawImage(m_bmp, 0, 0);
}
// This prevents the base class from doing OnPaintBackground.
// Since the OnPaint method paints the entire window there's no reason
// to let this go through. If we do it'll cause flashing.
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaintBackground"]/*'/>
protected override void OnPaintBackground(PaintEventArgs e)
{
}
// Called when it is time to draw the background. To take complete control of background
// drawing override this. To get called after the background is drawn by the base class
// register for the PaintBackground event.
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaintBackgroundII"]/*'/>
protected virtual void OnPaintBackground(ListPaintEventArgs e)
{
// Fill the background with the background colour
e.Graphics.Clear(this.BackColor);
if (PaintBackground != null)
PaintBackground(this, e);
}
// Called to draw a line item. To take complete control of drawing an item override this method.
// To let the base class draw the item first and then do your own additional work register for the
// PaintItem event.
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaintItem"]/*'/>
protected virtual void OnPaintItem(ListPaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle destRect = new Rectangle(); // Destination for the item image, if any
Rectangle srcRect = new Rectangle(); // Source region of the image to draw
Rectangle textRect = new Rectangle(); // Destination for the text
int lineIndent = 0; // How far the text is moved in from the left margin before drawing
Image imageToDraw = null;
string line1Text;
string line2Text;
// On the null case skip everything and just draw the separator line
if (e.Item == null)
goto DrawSeparator;
line1Text = GetStringProperty(e.Item, this.m_line1Property);
if (line1Text == null)
line1Text = e.Item.ToString();
line2Text = GetStringProperty(e.Item, this.m_line2Property);
if (line2Text == null)
line2Text = e.Item.ToString();
// Figure out if we're drawing an image per item from the object, or one for
// everything
imageToDraw = GetImageProperty(e.Item, this.m_itemImageProperty);
if (imageToDraw == null)
imageToDraw = m_itemImage;
// Calculate the position of the item image, if we have one, and the line indents
if (imageToDraw != null)
{
srcRect.X = 0;
srcRect.Y = 0;
srcRect.Width = imageToDraw.Width;
srcRect.Height = imageToDraw.Height;
// int vertPos = (m_itemHeight - m_itemImage.Height) / 2;
destRect.X = e.ClipRectangle.X + IMAGE_PADDING_X;
destRect.Y = e.ClipRectangle.Y + IMAGE_PADDING_Y;
// destRect.Y = (vertPos < 0) ? 0 : vertPos; // Center the image vertically in the line height. Handle the image being larger than the item height
// Account for an image that is taller than the item height
destRect.Height = (imageToDraw.Height > m_itemHeight - IMAGE_PADDING_Y) ? m_itemHeight - (IMAGE_PADDING_Y * 2) : imageToDraw.Height;
destRect.Width = destRect.Height;
// Set the text indent based on the image
lineIndent = IMAGE_PADDING_X + imageToDraw.Width + TEXT_PADDING_X; // Two pixels for the left indent of the image
}
else
{
// Set the text indent without using the image
lineIndent = TEXT_PADDING_X;
}
// Calculate the text rectangle
textRect.X = e.ClipRectangle.X + lineIndent;
textRect.Width = e.ClipRectangle.Width - TEXT_PADDING_X - textRect.X; // Allows for padding on the right edge too
textRect.Y = e.ClipRectangle.Y + 2;
textRect.Height = this.m_textHeightLine1;
// From here on we actually draw things. First the selected background, if necessary
if (e.Selected)
g.FillRectangle(m_brushSelBack, e.ClipRectangle);
// Draw the icon, if we have one
if (imageToDraw != null)
{
if (m_useTransparent)
g.DrawImage(imageToDraw, destRect, srcRect.Y, srcRect.Y, srcRect.Height, srcRect.Height,
GraphicsUnit.Pixel, m_imageAttributes);
else
g.DrawImage(imageToDraw, destRect, srcRect, GraphicsUnit.Pixel);
}
// Draw the text in a bounding rect to force it to truncate if too long
g.DrawString(line1Text, m_fontLine1, e.Selected ? m_brushSelText : m_brushText, textRect);
// Draw the second line
textRect.Y += m_textHeightLine1 + 3;
textRect.Height = this.m_textHeightLine2;
g.DrawString(line2Text, m_fontLine2, e.Selected ? m_brushSelText : m_brushText, textRect);
DrawSeparator:
// Draw the separator line
g.DrawLine(m_penSep, e.ClipRectangle.X, e.ClipRectangle.Y + e.ClipRectangle.Height,
e.ClipRectangle.X + e.ClipRectangle.Width, e.ClipRectangle.Y + e.ClipRectangle.Height);
// Let other people know it's time for them to draw
if (PaintItem != null)
PaintItem(this, e);
}
// Draw all the items.
private void DrawItems(Graphics g)
{
ListPaintEventArgs ListPaintEventArgs = new ListPaintEventArgs(g);
// Calculate our actual drawing area, accounting for the scroll bar
Rectangle rc = new Rectangle(0, 0, this.Width - m_scrollBarWidth, this.Height - 1);
// draw items that are visible
int curItem = 0;
for (int i = 0; (i < m_visibleCount); i++)
{
curItem = i + m_topItem;
if (curItem < m_list.Count)
{
// Calculate the drawing area for the item
ListPaintEventArgs.ClipRectangle = new Rectangle(rc.X,
rc.Y + (i * m_itemHeight),
rc.Width,
this.m_itemHeight);
// Get the item we'll be drawing with and whether it is selected
ListPaintEventArgs.Item = m_list[curItem];
ListPaintEventArgs.Selected = (m_selItem == curItem);
// Draw the item
OnPaintItem(ListPaintEventArgs);
}
}
}
// Recalculates the heights and visible counts for assorted items
// in the listbox.
// TODO: Get rid of this method by moving the rest of the items into the assorted
// properties.
private void RecalcItems(Graphics g)
{
// The text heights for a single line of text is the height of the font.
m_textHeightLine1 = g.MeasureString("W", this.m_fontLine1).ToSize().Height;
m_textHeightLine2 = g.MeasureString("W", this.m_fontLine2).ToSize().Height;
// The height for an individual item is two lines plus some padding
m_itemHeight = m_textHeightLine1 + m_textHeightLine2 + 5;
m_visibleCount = this.Height / m_itemHeight;
// Set the top item to draw to the current scroll position
m_topItem = m_scrollValue;
}
// Creates all the objects we need for drawing
private void CreateGdiObjects()
{
m_brushText = new SolidBrush(this.ForeColor);
m_brushSelText = new SolidBrush(this.m_selForeColor);
m_brushSelBack = new SolidBrush(this.m_selBackColor);
m_penSep = new Pen(this.m_separatorColor);
m_imageAttributes = new ImageAttributes();
}
// Creates a bitmap in memory to do our drawing. We'll draw on this first
// and then splat it to the screen.
private void CreateMemoryBitmap()
{
// Only create if don't have one and the size hasn't changed
if (m_bmp == null || m_bmp.Width != this.Width || m_bmp.Height != this.Height)
{
m_bmp = new Bitmap(this.Width - m_scrollBarWidth, this.Height);
// TODO: Figure out why this is here.
m_scrollBar.Left = this.Width - m_scrollBarWidth;
m_scrollBar.Top = 0;
m_scrollBar.Width = m_scrollBarWidth;
m_scrollBar.Height = this.Height;
}
}
I Hope this helps
How would I go about drawing a border with a specified width and color around a listbox?
Can this be done without overriding the OnPaint method?
Following Neutone's suggestion, here is a handy function to add and remove a Panel-based border around any control, even if it is nested..
Simply pass in the Color and size you want, and if you want a BorderStyle. To remove it again pass in Color.Transparent!
void setBorder(Control ctl, Color col, int width, BorderStyle style)
{
if (col == Color.Transparent)
{
Panel pan = ctl.Parent as Panel;
if (pan == null) { throw new Exception("control not in border panel!");}
ctl.Location = new Point(pan.Left + width, pan.Top + width);
ctl.Parent = pan.Parent;
pan.Dispose();
}
else
{
Panel pan = new Panel();
pan.BorderStyle = style;
pan.Size = new Size(ctl.Width + width * 2, ctl.Height + width * 2);
pan.Location = new Point(ctl.Left - width, ctl.Top - width);
pan.BackColor = col;
pan.Parent = ctl.Parent;
ctl.Parent = pan;
ctl.Location = new Point(width, width);
}
}
You can place a list box within a panel and have the panel serve as a border. The panel backcolor can be used to create a colored border. This doesn't require much code. Having a colored border around a form component can be an effective way of conveying status.
The problem with the ListBox control is that it does not raise the OnPaint method so you can not use it to draw a border around the control.
There are two methods to paint a custom border around the ListBox:
Use SetStyle(ControlStyles.UserPaint, True) in the constructor, then you can use the OnPaint method to draw the border.
Override WndProc method that handles operating system messages identified in the Message structure.
I used the last method to paint a custom border around the control, this will eliminate the need to use a Panel to provide a custom border for the ListBox.
public partial class MyListBox : ListBox
{
public MyListBox()
{
// SetStyle(ControlStyles.UserPaint, True)
BorderStyle = BorderStyle.None;
}
protected override void WndProc(ref Message m)
{
base.WndProc(m);
var switchExpr = m.Msg;
switch (switchExpr)
{
case 0xF:
{
Graphics g = this.CreateGraphics;
g.SmoothingMode = Drawing2D.SmoothingMode.Default;
int borderWidth = 4;
Rectangle rect = ClientRectangle;
using (var p = new Pen(Color.Red, borderWidth) { Alignment = Drawing2D.PenAlignment.Center })
{
g.DrawRectangle(p, rect);
}
break;
}
default:
{
break;
}
}
}
}
My question : How to disable a User Control to draw it's background (or Region)
Note : I already tried to override and empty OnPaintBackground or set background color to transparent.
I'm trying to bypass winform paint for my custom user controls in a custom container.
For that I thought to give a try to this : Beginners-Starting-a-2D-Game-with-GDIplus
My setup is :
A Form containing:
A User control (DrawingBoard)
A Container with elements I can drag and drop to this DrawingBoard (it's a listbox).
My render loop is inside the DrawingBoard with all elements specified in the previous link.
public DrawingBoard()
{
InitializeComponent();
//Resize event are ignored
SetStyle(ControlStyles.FixedHeight, true);
SetStyle(ControlStyles.FixedWidth, true);
SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);// True is better
SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true); // True is better
// Disable the on built PAINT event. We dont need it with a renderloop.
// The form will no longer refresh itself
// we will raise the paint event ourselves from our renderloop.
SetStyle(System.Windows.Forms.ControlStyles.UserPaint, false); // False is better
}
#region GDI+ RENDERING
public Timer t = new Timer();
//This is your BackBuffer, a Bitmap:
Bitmap B_BUFFER = null;
//This is the surface that allows you to draw on your backbuffer bitmap.
Graphics G_BUFFER = null;
//This is the surface you will use to draw your backbuffer to your display.
Graphics G_TARGET = null;
Size DisplaySize = new Size(1120, 630);
bool Antialiasing = false;
const int MS_REDRAW = 32;
public void GDIInit()
{
B_BUFFER = new Bitmap(DisplaySize.Width, DisplaySize.Height);
G_BUFFER = Graphics.FromImage(B_BUFFER); //drawing surface
G_TARGET = CreateGraphics();
// Configure the display (target) graphics for the fastest rendering.
G_TARGET.CompositingMode = CompositingMode.SourceCopy;
G_TARGET.CompositingQuality = CompositingQuality.AssumeLinear;
G_TARGET.SmoothingMode = SmoothingMode.None;
G_TARGET.InterpolationMode = InterpolationMode.NearestNeighbor;
G_TARGET.TextRenderingHint = TextRenderingHint.SystemDefault;
G_TARGET.PixelOffsetMode = PixelOffsetMode.HighSpeed;
// Configure the backbuffer's drawing surface for optimal rendering with optional
// antialiasing for Text and Polygon Shapes
//Antialiasing is a boolean that tells us weather to enable antialiasing.
//It is declared somewhere else
if (Antialiasing)
{
G_BUFFER.SmoothingMode = SmoothingMode.AntiAlias;
G_BUFFER.TextRenderingHint = TextRenderingHint.AntiAlias;
}
else
{
// No Text or Polygon smoothing is applied by default
G_BUFFER.CompositingMode = CompositingMode.SourceOver;
G_BUFFER.CompositingQuality = CompositingQuality.HighSpeed;
G_BUFFER.InterpolationMode = InterpolationMode.Low;
G_BUFFER.PixelOffsetMode = PixelOffsetMode.Half;
}
t.Tick += RenderingLoop;
t.Interval = MS_REDRAW;
t.Start();
}
void RenderingLoop(object sender, EventArgs e)
{
try
{
G_BUFFER.Clear(Color.DarkSlateGray);
UIPaint(G_BUFFER);
G_TARGET.DrawImageUnscaled(B_BUFFER, 0, 0);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
#endregion
Then my elements get the event fired and try to draw what I would like:
public override void UIPaint(Graphics g)
{
Pen p = new Pen(Color.Blue,4);
p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
g.DrawLines(p, new Point[] { new Point(Location.X, Location.Y), new Point(Location.X + Width, Location.Y), new Point(Location.X + Width, Location.Y + Height), new Point(Location.X, Location.Y + Height), new Point(Location.X, Location.Y - 2) });
g.DrawImageUnscaled(GDATA.GetWindowImage(), Location);
}
here is what happening on my DrawingBoard :
As I can't post image ... here is the link : http://s8.postimage.org/iqpxtaoht/Winform.jpg
The background is DarkSlateGray because my G_BUFFER state to clear each tick with that, Ok
The blue rectangle is what I paint, but it get cropped, KO
The Texture is cropped, KO
The region that crop my drawing is the control size.
So from there I've tried everything I could to disable WinForm to make some magic drawing in background. Tried to override and empty everything that got paint/update/refresh/invalidate/validate on Form/DrawingBoard/Elements but nothing allowed me to get my texture or drawing to not get cropped by the control background : (
I also tried to set the background of the Element as transparent and also to set Form.TransparencyKey = blabla with each element BackColor = blabla. But failed each time.
I'm certainly missing something : / But I don't know what.
Why don't you want to draw the background? To avoid problems with flickering in my custom control (derived from class Control), here's what I did:
(1) override OnPaintBackground:
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
(2) Draw in an offscreen Bitmap and then transfer it to the screen:
Bitmap _completeFrame;
protected override void OnPaint(PaintEventArgs pe)
{
DrawFrame(); // draws onto _completeFrame, calling new Bitmap()
// when _completeFrame is null or its Size is wrong
var g = pe.Graphics;
g.DrawImage(_completeFrame, new Point());
}
In this picture...
... you can see next to each "Line Color" label there is a colored circle.
The colored circle is, in my project, a Swatch. Here is the entire code file for Swatch:
public class Swatch : System.Windows.Forms.Panel
{
/*private int _Radius = 20;
[System.ComponentModel.Category("Layout")]
public int Radius
{
get { return _Radius; }
set { _Radius = value; }
} */
private System.Drawing.Color _BorderColor = System.Drawing.Color.Transparent;
[System.ComponentModel.Category("Appearance")]
public System.Drawing.Color BorderColor
{
get { return _BorderColor; }
set { _BorderColor = value; }
}
private System.Drawing.Color _FillColor = System.Drawing.Color.Blue;
[System.ComponentModel.Category("Appearance")]
public System.Drawing.Color FillColor
{
get { return _FillColor; }
set { _FillColor = value; }
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
System.Drawing.Rectangle RealRect = new System.Drawing.Rectangle(e.ClipRectangle.Location, e.ClipRectangle.Size);
RealRect.Inflate(-1, -1);
int Radius = Math.Min(RealRect.Size.Height, RealRect.Size.Width);
System.Drawing.Rectangle SqRect = new System.Drawing.Rectangle();
SqRect.Location = RealRect.Location;
SqRect.Size = new System.Drawing.Size(Radius, Radius);
System.Drawing.Drawing2D.CompositingQuality PrevQual = e.Graphics.CompositingQuality;
using (System.Drawing.SolidBrush Back = new System.Drawing.SolidBrush(this.FillColor))
{
using (System.Drawing.Pen Pen = new System.Drawing.Pen(new System.Drawing.SolidBrush(this.BorderColor)))
{
//e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.FillEllipse(Back, SqRect);
e.Graphics.DrawEllipse(Pen, SqRect);
}
}
e.Graphics.CompositingQuality = PrevQual;
}
public Swatch()
{
this.SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true);
this.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(System.Windows.Forms.ControlStyles.ResizeRedraw, true);
this.SetStyle(System.Windows.Forms.ControlStyles.SupportsTransparentBackColor, true);
this.DoubleBuffered = true;
}
}
Each row is a UserControl which consists of a TableLayoutPanel, labels, a Swatch control, and a NumericUpDown box.
There about 10 rows and they are placed in TableLayoutPanel, which sits inside a TabPage on a tab control. The tab page has AutoScroll set to true so that overflow causes the tab page to scroll.
The problem is that whenever I run the application and scroll up and down, the Swatches (the colored circles) tear and show all sorts of artifacts, as seen in the picture above. I'd like to have clean scrolling with no rendering artifacts.
I've tried using SetStyle (as suggested here Painting problem in windows form) but it has had no effect.
The UserControl (each row) has DoubleBuffered set to true, and that too has had no effect either.
I fear I am missing something rather obvious.
The problem is that you calculate the radius of the circle based on the clipping rectangle. So when the line is only partially visible a bad value is resulted.
You should calculate it based on the real rectangle, the one provided by the base class, and let it being clipped normally.
I want to draw a grid in my panel. The graphics object (screen) that i created with bitmap isn't drawing in my panel. I tried with debugging to look if the screen wasn't drawing but that wasn't the case.
I tried creating the graphic object from the panel createGraphic method and the parameter painteventargs from the panel paint method. Both times when I used it to draw it with OnPaint it took too long.
public Main()
{
InitializeComponent();
backBuffer = new Bitmap(drawPanel.Width, drawPanel.Height);
screen = Graphics.FromImage(backBuffer);
sizeGridPoints = 2;
lenghtBetweenGridPoints = 10;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
screen.Clear(Color.Black);
DrawGrid();
}
private void DrawGrid()
{
for(int x = lenghtBetweenGridPoints; x < drawPanel.Width; x += lenghtBetweenGridPoints)
{
for(int y = lenghtBetweenGridPoints; y < drawPanel.Height; y+= lenghtBetweenGridPoints)
{
screen.FillEllipse(new SolidBrush(Color.Green), x, y, sizeGridPoints, sizeGridPoints);
}
}
}
If you create a Graphics object from a bitmap, it will draw on this bitmap, not on your user interface. Instead, use the Graphics object from the PaintEventArgs e of your OnPaint method, to draw directly on the form or a control.
e.Graphics.FillEllipse(new SolidBrush(Color.Green), x, y, sizeGridPoints, sizeGridPoints);
You should never create your own Graphics object.
Create your own grid control:
public class GridPanel : Panel
{
public GridPanel()
{
DoubleBuffered = true; // Speeds up drawing, e.g. when panel is resized.
// Set default colors
BackColor = Color.Black;
ForeColor = Color.Green;
}
private int _lenghtBetweenGridPoints = 20;
public int LenghtBetweenGridPoints
{
get { return _lenghtBetweenGridPoints; }
set {
if (value != _lenghtBetweenGridPoints) {
_lenghtBetweenGridPoints = value;
Invalidate(); // Redraw the grid.
}
}
}
private int _sizeGridPoints = 3;
public int SizeGridPoints
{
get {
return _sizeGridPoints;
}
set {
if (value != _sizeGridPoints) {
_sizeGridPoints = value;
Invalidate(); // Redraw the grid.
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
// e.Graphics.Clear(Color.Black); Not necessary. We use the BackColor of the panel.
if (LenghtBetweenGridPoints > 0 && SizeGridPoints > 0) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Optional.
using (var brush = new SolidBrush(ForeColor)) { // We use the ForeColor of the panel.
for (int x = LenghtBetweenGridPoints; x < Width; x += LenghtBetweenGridPoints) {
for (int y = LenghtBetweenGridPoints; y < Height; y += LenghtBetweenGridPoints) {
e.Graphics.FillEllipse(brush, x, y, SizeGridPoints, SizeGridPoints);
}
}
}
}
}
}
Once it has been compiled, it will automatically appear in toolbox window and you can drag and drop it on your form. You will even be able to edit the properties LenghtBetweenGridPoints and SizeGridPoints in the properties window.
You could also simply use the already available BackColor and ForeColor properties of the panel for the grid. This would allow you to set the colors in the properties window as well. Don't forget to dispose brushes that you have created.
Important: Do not call OnPaint directly. Instead, call the Invalidate or Refresh methods of the object you want to redraw. The point is that Windows decides when to call OnPaint. E.g. if Invalidate is called too frequently (e.g. 5 times in 1/60 seconds), Windows might decide to not call OnPaint every time, as this would create lag. On the other hand, when the user resizes the panel, Windows will call OnPaint automatically. If you restore a window that was minimized, this will re-paint the control as well. Otherwise it would remain black.