How come the graphics is not drawing onto the panel - c#

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.

Related

C# Winforms - Check when moveable drawn image is over a label

I have an image drawn on the form which can be moved by the arrow keys.
It's created in the paint method as such:
int _x = 500; // initial x co-ordinate
int _y = 600; // initial y co-ordinate
private void testForm_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(new Bitmap("carImage.png"), _x, _y, 150, 100);
}
I also have some labels on the form, and the aim is that when the image is moved over a label and space is pressed, the text on the label changes.
What I'm confused about is how to check if the image is at the right location when space is pressed, i.e. if it is on top of the label.
I tried a few different ways, including this:
case Keys.Space:
foreach (Control control in this.Controls)
{
if (control is Label)
{
for (int i = _x; i < _x + 150; i++)
{
for (int j = _y; j > _y - 100; j--)
{
// If carImage within bounds of control
if (control.Bounds.Contains(i, j))
control.Text = "newText";
}
}
}
}
But this doesn't seem to work in my code, and I think even if it did it may be slow, as it has to iterate through so many times.
I also tried using
control.Bounds.IntersectsWith(carImage.Bounds ...)
but I'm not sure what object type the image should be in order to use it as the method argument.
I tried making the image a type Bitmap, but it wouldn't have a .Bounds property (there is only GetBounds() ). I think I could use carImage.Height etc., but I'm still not sure how I would check whether they overlap using this.
So, two questions:
Is there another way to check if any of the image co-ordinates overlap with the label bounds, and, if not,
What type to set the image as so that it can be called in control.Bounds.IntersectsWith(), and how exactly to get that method to work with the image bounds (i.e. what should be inside the parentheses).
Any help is much appreciated!
Try this way:
Bitmap _image = new Bitmap("carImage.png");
int _x = 500; // initial x co-ordinate
int _y = 600; // initial y co-ordinate
private void testForm_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_image, _x, _y);
}
private void CheckIntersect()
{
Rectangle imageBounds = new Rectangle(_x, _y, _image.Width, _image.Height);
if (label1.Bounds.IntersectsWith(imageBounds))
control.Text = "newText";
}

Adding paint code to trackbar control

I want to add text labels onder the tick marks of a trackbar control. Initially everything appears fine but when a drag the thumb of the trackbar my text labels disappear. What is happening here ?
Here is my code :
public class DateTimeTrackBar : TrackBar
{
public DateTimeTrackBar()
{
:
SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
}
:
protected override void OnPaint(PaintEventArgs e)
{
base.SetStyle(ControlStyles.UserPaint, false);
base.Refresh();
if (ShowLabels)
DrawLabels(e);
base.SetStyle(ControlStyles.UserPaint, true);
//base.OnPaint(e);
}
protected virtual void DrawLabels(PaintEventArgs e)
{
int nNumTicks = GetNumTicks(this);
if (nNumTicks > 0)
{
PointF[] TickLocs = GetTickLocations(nNumTicks);
string[] TickLabels = GetTickLabels(nNumTicks);
using (Font ArialFnt = new Font("Arial", 6, FontStyle.Regular))
{
using (Brush GrayBrush = new SolidBrush(Color.Gray))
{
float fTickLabelLocOffset;
for (int i = 0; i < nNumTicks; i++)
{
if (!ShowMinMaxLabels && ((i == 0) || (i == (nNumTicks - 1))))
continue;
if (i == 0)
fTickLabelLocOffset = 0.0f;
else
{
SizeF Size = e.Graphics.MeasureString(TickLabels[i], ArialFnt);
if (i == (nNumTicks - 1))
fTickLabelLocOffset = Size.Width;
else
fTickLabelLocOffset = (Size.Width / 2.0f);
}
PointF TickLabelLoc = new PointF(TickLocs[i].X - fTickLabelLocOffset, TickLocs[i].Y + 8);
e.Graphics.DrawString(TickLabels[i], ArialFnt, GrayBrush, TickLabelLoc);
}
}
}
}
}
:
}
The standard convention here would be to not set the UserPaint style, to call base.OnPaint() in your override method, and then render your custom graphics after the call to base.OnPaint() returns, similar to this:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (ShowLabels)
DrawLabels(e);
}
Based on the documentation for Control.SetStyle() at https://msdn.microsoft.com/en-us/library/system.windows.forms.control.setstyle(v=vs.110).aspx as well as the ControlStyles enumeration at https://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles(v=vs.110).aspx, consider the following:
You should not be calling SetStyle() repeatedly to change the flags (as happens in your OnPaint() method). These should be set once, when the control is initialized.
When you set the UserPaint flag to true, you are telling Windows the following. This is likely not your intent:
If true, the control paints itself rather than the operating system
doing so. If false, the Paint event is not raised. This style only
applies to classes derived from Control.

Why am I having trouble writing a pixel to a picturebox in Visual C#?

I am writing an emulator program, and the virtual display is supposed to be able to take in 3 bytes of color data and display the correct color pixel, similar to how a real screen works. But when I set up some scroll bars to test the generation of pixels nothing happens. Here is my code and a screenshot of the form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TSC_Multi_System_Emulator
{
public partial class Form1 : Form
{
private PictureBox Display = new PictureBox();
string #emulationfolderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Bitmap screen = new Bitmap(#Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\Resource_Folder\" + #"FirstFrame.bmp");
int x = 0;
int y = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e) {
// Dock the PictureBox to the form and set its background to black.
Display.BackColor = Color.Black;
// Connect the Paint event of the PictureBox to the event handler method.
// Add the PictureBox control to the Form.
this.Controls.Add(Display);
}
public void DigitalGraphicsDisplay(int red, int green, int blue) {
Graphics g = Display.CreateGraphics();
screen.SetPixel(x, y, Color.FromArgb(red, green, blue));
g.DrawImage(screen, 0, 0, screen.Width, screen.Height);
g.Save();
if (x < screen.Width)
{
x = x + 1;
}
else if (x == screen.Width)
{
x = 0;
if (y < screen.Height)
{
y = y + 1;
}
else if (y == screen.Height)
{
y = 0;
}
}
}
private void button1_Click(object sender, EventArgs e){
int rchannel = redControl.Value;
int gchannel = greenControl.Value;
int bchannel = blueControl.Value;
DigitalGraphicsDisplay(rchannel, gchannel, bchannel);
}
}
}
UPDATE:
The code is now working somewhat, but I can't test the code using just a test button. I had to use the exact code given to me in the first answer, which only displayed a gradient, I wonder what I am doing wrong... :(
public partial class Form1 : Form
{
string #emulationfolderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Bitmap screen = new Bitmap(#Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\Resource_Folder\" + #"FirstFrame.bmp");
int x = 0;
int y = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e) {
// Dock the PictureBox to the form and set its background to black.
Display.BackColor = Color.Black;
// Connect the Paint event of the PictureBox to the event handler method.
// Add the PictureBox control to the Form.
this.Controls.Add(Display);
}
public void DigitalGraphicsDisplay(int red, int green, int blue)
{
if (Display.Image == null)
{
Bitmap NewBMP = new Bitmap(Display.ClientRectangle.Width, Display.ClientRectangle.Height);
using (Graphics g = Graphics.FromImage(NewBMP))
{
g.Clear(Color.White);
}
Display.Image = NewBMP;
}
(Display.Image as Bitmap).SetPixel(x, y, Color.FromArgb(red, green, blue));
Display.Invalidate();
x++;
if (x >= Display.Image.Width)
{
x = 0;
y++;
if (y >= Display.Image.Height)
{
y = 0;
}
}
}
private void button1_Click(object sender, EventArgs e){
Boolean a = false;
int b = 0;
do
{
DigitalGraphicsDisplay(51, 153, 102);
if (b == 10000)
{
a = true;
}
b = b + 1;
} while (a);
}
}
}
All I am getting is a white picturebox with nothing else in it...
(The gradient code did work though)
It looks like you are trying to draw directly on the PictureBox control itself.
Instead you should have an Image assigned to the PictureBox and then draw on the image.
Try changing your code as shown below. (Including the click event for testing.)
Note, the PictureBox keeps the reference to the image directly so you don't need a separate screen image in your class, unless you have a different purpose for it.
Also, this uses Bitmap.SetPixel() which is an extremely slow way to set pixels. There is a much faster but slightly more complex way, in these other links:
SetPixel is too slow. Is there a faster way to draw to bitmap?
Work with bitmaps faster in C#
Remember your button click will only draw one pixel at a time.
So be sure to look carefully:
Running my test code within the click event will yield this:
int x = 0;
int y = 0;
public void DigitalGraphicsDisplay(int red, int green, int blue)
{
if (Display.Image == null)
{
Bitmap NewBMP = new Bitmap(Display.ClientRectangle.Width, Display.ClientRectangle.Height);
using (Graphics g = Graphics.FromImage(NewBMP))
{
g.Clear(Color.White);
}
Display.Image = NewBMP;
}
(Display.Image as Bitmap).SetPixel(x, y, Color.FromArgb(red, green, blue));
Display.Invalidate();
x++;
if (x >= Display.Image.Width)
{
x = 0;
y++;
if (y >= Display.Image.Height)
{
y = 0;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
// Temporary code to show that it works (Due to Bitmap.SetPixel() it will be slow)
for (int I = 1; I < Display.ClientRectangle.Width * Display.ClientRectangle.Height; I++)
DigitalGraphicsDisplay((I/255)%255, (I % Display.ClientRectangle.Width) % 255, 127);
}
UPDATE: Per your comment, try this sample code:
private void button1_Click(object sender, EventArgs e)
{
Boolean a = true;
int b = 0;
do
{
DigitalGraphicsDisplay(51, 153, 102);
if (b == 10000)
{
a = false;
}
b = b + 1;
} while (a);
}
public void DigitalGraphicsDisplay(int red, int green, int blue) {
Graphics g = Display.CreateGraphics();
screen.SetPixel(x, y, Color.FromArgb(red, green, blue));
g.DrawImage(screen, 0, 0, screen.Width, screen.Height);
g.Save();
All possible mistakes in one go..
Never use CreateGraphics to draw persistent Graphics! Always either go for the Paint event or draw into the Image.
Graphics.Save does not save any drawn pixels. It saves the state of the Graphics object, which does not contain graphics but is a tool to write into a related bitmap. The state includes scale, rotation, smoothing-mode and then some..
You already write into the Bitmap so you can simply make it your new PictureBox.Image..
Or the PictureBox.BackgroundImage.
And, as I said, you can instead write on top of both that is onto the PBox's surface. For this use the Paint event, Invalidate to trigger it and class level variables to hold the necessary data..
The latter is for graphics that will change a lot, the two former ones are for changes that accumulate.
Control.CreateGraphics is for transient graphics only, like a rubber-band line or a cursor cross..

Datagridview to draw wafer map

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.

Custom control in a UserControl does not render correctly

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.

Categories