Drawing an image in this method? - c#

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
NumberOfBets++;
if ((e.X >= 40 && e.X <= 125) && (e.Y >= 0 && e.Y <= 26))
{
bettingStatus[0]++;
for (int x; x < 10; x++)
{
Graphics graphic = (???)
}
}
I'm trying to draw an image in this MouseDown method. I have NO clue what goes in the "(???)" part.

// Create a Graphics object for the pictureBox1 control.
Graphics g = pictureBox1.CreateGraphics();
Read more: MSDN: Graphics Class
You should draw in the Paint event, because if you don't, you will loose your drawings if the control is repainted. The PaintEventArgs passed to the Paint event handler has a Property named Graphics (of type System.Drawing.Graphics) which you can draw on.

You need to get the Graphics from somewhere, probably pictureBox1 in your case:
Graphics graphic = pictureBox1.CreateGraphics();
...But are you sure you want to be drawing in a MouseDown event handler? It won't be repainted if any part gets redrawn. You'd probably be better off doing all your drawing in a Paint event handler and setting a flag in MouseDown instead. Then invalidate the region you want to be redrawn to draw the new image.
Or, if your images are going to be more static, you can create a Bitmap, draw on that, then set the Image of your PictureBox to point to the Bitmap instead. For example:
Bitmap bmp = new Bitmap(200, 100);
Graphics graphics = Graphics.FromImage(bmp);
//do drawing here
pictureBox1.Image = bmp;

Related

Drawing rectangle is overwriting existing bitmap c#

I have a grid already drawn to a picturebox and I wish to draw a rectangle in the cell the mouse is clicked in. I'm trying to do this by drawing a rectangle in the top left corner of the cell and having it fill the entire cell.
However when I click the grid disappears.
How do I make it so the grid stays?
and is there an obvious better method for doing this?
The only way to redraw the grid is by pressing the button again but I want it to stay there.
Thanks.
You need to create a Graphics
Image bm;// Or Bitmap
using (var graphics = Graphics.FromImage(bm))
{
Draw(rects, graphics);
}
private static void Draw(List<Rectangle> rectangles, Graphics graphics)
{
Pen redPen = new Pen(Color.Red, 1);
foreach (var rect in rectangles)
{
graphics.DrawRectangle(redPen, rect);
}
}
At every mouse click in the mouse_click event at first you dispose the image of the picture box and then again assign the bitmap to the picturebox image otherwise you might face out of memory exception if you click on the grid too many times
private void InitializePictureBoxImage(PictureBox pictureBox)
{
if(pictureBox.Image != null)
{
var image = pictureBox.Image; // cache it to dispose
pictureBox.Image = null; // don't dispose an used object
image.Dispose(); // and dispose of it
}
Bitmap bitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
pictureBox.Image = bitmap; // assign bitmap to image
}
Then call the method in the mouse_click event which you used to draw the picture box when you press the Generate Grid button in your form. Afterwards use graphics inside the mouse_click event to colour the grid which you pressed taking the X-Y coordinate from MouseEventArgs.
Altogether I think it will be something like this
private void Mouse_ClickEvent(object sender, MouseEventArgs e)
{
InitializePictureBoxImage(pictureBox);
DrawThePictureBoxImage(pictureBox);
using (Graphics graphics = Graphics.FromImage(pictureBox.Image))
{
Color color = Color.Blue;
color = Color.FromArgb(150, color.R, color.G, color.B); // lower the opacity so the writing in the grid is visible
var brush = new SolidBrush(color);
// Calculate the starting X-Y coordinate and Width,Height of the grid to color
graphics.FillRectangle(brush, startX, startY,
width, height);
}

How can i save graphics.copyfromscreen to a bitmap file on hard disk?

In a button click event:
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
painting = false;
pictureBox2.Invalidate();
}
Then in the paint event:
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
Point pnt;
if (rect.Width > 10 && rect.Height > 10)
{
pnt = PointToScreen(pictureBox1.Location);
e.Graphics.Clear(Color.White);
e.Graphics.CopyFromScreen(pnt.X + rect.X, pnt.Y + rect.Y, rect.X, rect.Y, new Size(rect.Width, rect.Height));
}
}
I want to save to a bitmap file not the whole pictureBox2.Image but only the CopyFromScreen part.
This is a screenshot of pictureBox2
The pictureBox2 in the screenshot borders are also the white color around the image. But I want to save only the image not the whole pictureBox2.Image and to do it before the pictureBox2 paint event maybe in the button click event. So the image only will be saved to a bitmap.
If all what you want is to save something from the Graphics object (whether you have used the CopyFromScreen call or not), you can refer to something that have been posted here before:
Saving System.Drawing.Graphics to a png or bmp
Or even there: How does the Graphics CopyFromScreen method copy into a bitmap?
Please note that you don't necessarily need to use the PictureBox Paint event handler for that.
Let me know whether this answers your question =]

Reset last draw graphics on text change

i have a four text boxes for draw rectangle which is x,y,height and width and i want to draw rectangle on text change but the Rectangle is not drawn when i reset a image ( `picturebox1.Image = bkp ) what i am doing wrong help me guyz?
if (txtHeight.Text != "" && txtLeftMargin.Text != "" && txtTopMargin.Text != "" && txtWidth.Text != "")
{
pictureBox1.Image = bkp;
Pen pen = new Pen(Color.Red);
Graphics g = pictureBox1.CreateGraphics();
g.DrawRectangle(pen, Convert.ToInt16(txtLeftMargin.Text), Convert.ToInt16(txtTopMargin.Text), Convert.ToInt16(txtHeight.Text), Convert.ToInt16(txtWidth.Text));
}
Try using this code:
Image backgroundImage = (Image)bkp.Clone();
using (Graphics gfx = Graphics.FromImage(backgroundImage))
using (Pen pen = new Pen(Color.Red))
{
gfx.DrawRectangle(pen,
Convert.ToInt16(txtLeftMargin.Text),
Convert.ToInt16(txtTopMargin.Text),
Convert.ToInt16(txtHeight.Text),
Convert.ToInt16(txtWidth.Text));
}
pictureBox1.Image = backgroundImage;
Check that the "bkp" -image isn't empty and that it's bigger than 1x1, or you can't see a thing. Otherwise it should work. I tested it with "Image.FromFile()", where I loaded an image from my HDD to "backgroundImage"-variable. The dimensions of the image set the maximum draw area.
As so many others you don't draw the right way:
Everything you draw in winforms must either be drawn in the control's paint event or be triggered from there. So you must:
keep the geometry data for your Rectangle from the textchange event somewhere (if that is what you are doing),
put the drawing in the paint event (using the e.Graphics from the event argument) and
trigger it by invalidating the control..

Persistent graphics WinForms

I've a WinForms application on wich i have to draw some lines between controls. These lines need to be persistent, so i override the form OnPaint() event.
The problem is that, the re-draw of the lines aren't very smooth.
I'm creating the graphics as follows:
Graphics g;
g = this.CreateGraphics();
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.FillRectangle(Brushes.White, this.ClientRectangle);
And drawing the lines as follows:
public void lineDraw(Control L1, Control L2) {
using (Pen pen = new Pen(Color.Black, 4)) {
pen.StartCap = System.Drawing.Drawing2D.LineCap.Flat;
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
int x1, x2, y1, y2;
//choose x/y coordinates
g.DrawLine(pen, x1, y1, x2, y2);
}
}
Is there any property i can set to improve the smoothness of the drawn graphics?
Goal
An image is shown on a control (or form).
Invalidation
Any time the control (or form) is resized, minimalized/maximalized, partically obscured or moved around, it must be (partially) redrawn. When this happens the part of the control that must be redrawn is said to be invalidated.
When invalidated the control does something like this:
Call OnPaintBackground: this fills the invalidated region with the background color.
Call OnPaint: this draws the text and graphics on top of the background.
Why OnPaint causes flickering
You have overridden the OnPaint method of the control. Every time the control is redrawn you see a flash of the control with only its background color drawn in it. That is after OnPaintBackground has been called and before OnPaint has been called.
The solutions
If you have a static image (i.e. it never changes):
In the Load event: create a new Bitmap object.
Fill it with the background color and draw the lines and shapes on it.
Assign this Bitmap object to the control's BackgroundImage property.
If you have a static image that must resize when the control resizes:
Override the OnResize method and create the new Bitmap in there. Use the control's ClientSize property for the size of the Bitmap.
Fill it with the background color and draw the lines and shapes on it.
Assign this Bitmap object to the control's BackgroundImage property.
If you have an animated image:
In the Load event set the DoubleBuffered property of the control to true. Setting this prevents the flicker you saw as it uses an invisible buffer to draw the control.
Override the control's OnPaint method. Get the Graphics context of the control and draw the lines and shapes directly on the control.
Create and enable a new Timer object and in its callback method call the control's Invalidate method followed by the Update method (as shown here). Set the timer to fire, for example, every 40 ms.
You probably shouldn't use CreateGraphics here, and more importantly, don't use a local variable to store the graphic object. Use the graphic object obtained from the paint event and invalidate as needed.
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(Color.White);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (Pen pen = new Pen(Color.Black, 4)) {
pen.StartCap = Drawing2D.LineCap.Flat;
pen.EndCap = Drawing2D.LineCap.ArrowAnchor;
int x1, x2, y1, y2;
//choose x/y coordinates
e.Graphics.DrawLine(pen, x1, y1, x2, y2);
}
base.OnPaint(e);
}
This's my way, its works for me
//FormMain.cs
private const float DisplayRatio = 6;
private Bitmap _bmpDisp; //use an in-memory bitmap to Persistent graphics
private Graphics _grpDisp4Ctl;
private Graphics _grpDisp4Bmp;
private Point _ptOldDsp;
private void FormMain_Shown(object sender, EventArgs e)
{
_grpDisp4Ctl = CreateGraphics();
_grpDisp4Ctl.SetHighQulity();
_bmpDisp = new Bitmap(ClientSize.Width, ClientSize.Height);
_grpDisp4Bmp = Graphics.FromImage(_bmpDisp);
_grpDisp4Bmp.SetHighQulity();
_ptOldDsp = new Point(
(int)((MousePosition.X - SystemInformation.VirtualScreen.Left) / DisplayRatio),
(int)((MousePosition.Y - SystemInformation.VirtualScreen.Top) / DisplayRatio)
);
}
private void UpdateDisplay(MouseHookEvent mhep) //your implement
{
var ptNew = mhep.Position;
ptNew.Offset(new Point(-SystemInformation.VirtualScreen.Left, -SystemInformation.VirtualScreen.Top));
ptNew.X = (int)(ptNew.X / DisplayRatio);
ptNew.Y = (int)(ptNew.Y / DisplayRatio);
_grpDisp4Ctl.DrawLine(Pens.White, _ptOldDsp, ptNew); //draw smooth lines to mem and ui
_grpDisp4Bmp.DrawLine(Pens.White, _ptOldDsp, ptNew);
_ptOldDsp = ptNew;
}
private void FormMain_Paint(object sender, PaintEventArgs e)
{
// like vb6's auto redraw :)
e.Graphics.DrawImage(_bmpDisp, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
}
//common.cs
internal static void SetHighQulity(this Graphics g)
{
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
}
I know it's an older post, but you can also try setting DoubleBuffered property of the form to TRUE, read the following:
"Buffered graphics can reduce or eliminate flicker that is caused by
progressive redrawing of parts of a displayed surface. Buffered
graphics require that the updated graphics data is first written to a
buffer. The data in the graphics buffer is then quickly written to
displayed surface memory. The relatively quick switch of the displayed
graphics memory typically reduces the flicker that can otherwise
occur."

C# Drawing - best solution

Today I am trying to solve problem with a blinking panel, when I draw onto it.
Lots of threads I read, like these:
how to stop flickering C# winforms,
Double buffering with Panel,
How can I draw on Panel so it does not blink?
So I tried to draw onto PictureBox, MyPanel with doubleBuffered, but the best solution I found, when I read, that I can't use g.Clear() every time, after that, even on non-doubleBuffered panel, blinking disappeared.
I even read, that I should free Graphics after draw is done. So I use everywhere using(Graphics g = panel.CreateGraphics()).
So my question, is it a great idea to create graphics for bitmap only when I draw something to it? Because before I created Bitmap, and Graphics (only for this bitmap, not for all components), so I had Graphics available for this bitmap every time
Here is my code:
public void newSizeDrawing()
{
Size size = collector.getLetterSize(selectedName);
Size drawingSize = new Size(size.Width * (pixelSizeArray[pixelSize] + 1),size.Height * (pixelSizeArray[pixelSize] + 1));
bitmapDraw = new Bitmap(drawingSize.Width, drawingSize.Height);
int width = (this.MinimumSize.Width - panelDraw.MinimumSize.Width) + drawingSize.Width + 10;
int height = (this.MinimumSize.Height - panelDraw.MinimumSize.Height) + drawingSize.Height + 10;
this.Size = new Size(
(width > this.MinimumSize.Width) ? width : this.MinimumSize.Width,
(height > this.MinimumSize.Height) ? height : this.MinimumSize.Height);
zeroDrawPosition = new Point((panelDraw.Size.Width - bitmapDraw.Width) / 2 - 1, (panelDraw.Size.Height - bitmapDraw.Height) / 2 - 1);
using (Graphics g = panelDraw.CreateGraphics())
{
g.Clear(panelDraw.BackColor);
}
redrawDrawingLetter();
}
public void redrawDrawingLetter()
{
bool[][] grid = collector.getArray(selectedName);
using (Graphics graphicDraw = Graphics.FromImage(bitmapDraw))
{
graphicDraw.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
graphicDraw.Clear(panelDraw.BackColor);
int pxSize = pixelSizeArray[pixelSize];
for (int y = 0; y < grid.Length; y++)
{
for (int x = 0; x < grid[y].Length; x++)
{
graphicDraw.FillRectangle((grid[y][x] ? Brushes.Black : Brushes.White), x * (pxSize + 1), y * (pxSize + 1), pxSize, pxSize);
}
}
}
redrawDrawingPanel();
}
private void redrawDrawingPanel()
{
using (Graphics g = panelDraw.CreateGraphics())
{
if (bitmapDraw != null)
g.DrawImage(bitmapDraw, zeroDrawPosition);
}
}
private void panelDraw_Paint(object sender, PaintEventArgs e)
{
redrawDrawingPanel();
}
Nobody can explain to me how to draw in C# the best way. So maybe my code isn't good, but that is reason why I asking how to do it correctly.
newSizeDrawing is called by myself only, when user click on + or - button. I have bool double-dimension array if pixel is on or off. This is program for drawing letters for microchips and LED display (often 8px height of letter).
I wrote a method that checks if the mouse moved from one "pixel" to another, so I don't redraw it after every call mouseMove event, because "pixel" can be from 10x10 px to 30x30 px.
private void panelDraw_Paint(object sender, PaintEventArgs e)
{
redrawDrawingPanel();
}
This is fundamentally wrong. The Paint event passes e.Graphics to let you draw whatever you want to paint. When you turn on double-buffering, e.Graphics refers to a bitmap, it is initialized with the BackColor. You then proceed to drawing using another Graphics object you got from CreateGraphics(). That one draws directly to the screen.
The flicker effect you see if very pronounced. For a split second you see what the other Graphics context draws. Then your panelDraw_Paint() method returns and Winforms draws the double-buffered bitmap. There's nothing on it so it immediately erases what you drew.
Modify the redrawDrawingPanel() method and give it an argument of type Graphics. Pass e.Graphics in the call. And only use that Graphics object, remove all calls to CreateGraphics().

Categories