Reset last draw graphics on text change - c#

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..

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);
}

WinForms create transparent clearable pictureBox overlays

I want to create 2 transparent overlays for pictureBox in WinFrom application so I can separately draw on both and I can also clear it when I want to blank transparent overlay.
On one overlay I draw rectangles. I want these rectangles there all time.
On second overlay I draw circles but I just want to draw 1 circle and after user input clear this circle and draw another.
For now I'm using
var transparentOverlay = pictureBox.createGraphics();
But I don't know how to clear an overlay to blank transparent graphics.
I tried
transparentOverlay.Clear(Color.Transparent) which turned all overlay to black
pictureBox.Invalidate() which cleared all graphics from both overlays so my rectangles remain where they were
use some backup graphics which I created before any drawing and clear my overlay by assigning this graphics to it transparentOverlay = transparentOverlayBackup but this did nothing, all rectangles and all circles remain at their places
Is there a way to create clearable transparent graphics sticked to pictureBox?
EDIT:
I have a picture with text in that picture box. And what I want to do is to draw rectangles around words of the text and this rectangles should remain all the time over the picture.
Than I want to draw a single circle and wait for user to click on a screen. This is all ok but when user click on a screen, I want to clear that circle and draw another.
//this method I call by click on a button to start annotation
private void ExpertAnnotate(object sender, EventArgs e)
{
var pen = new Pen(Color.Black, 1);
if (!annotationIsRunning) //check if annotation is in process or not
{
annotationIsRunning = true;
annotationOverlay = pictureBox.CreateGraphics(); //create transparent overlay for drawing
//draw rectangles around all words in text (AOIs)
annotationAOIs.ForEach(a =>
{
annotationOverlay.DrawRectangle(pen, a.Start.X, a.Start.Y, (a.End.X - a.Start.X), (a.End.Y - a.Start.Y));
});
//subscribe mouseMove and mouseClick events on pictureBox
pictureBox.MouseMove += HighlightAOI;
pictureBox.MouseClick += SelectAOI;
}
//define brushes for drawing circles (fixations)
var brush = new SolidBrush(Color.FromArgb(128, Color.BlueViolet));
var dotBrush = new SolidBrush(Color.DarkBlue);
pen.Color = Color.Blue;
long sizeOfFixation;
var f = Fixations[fixationCounter - 1]; //get current fixation to draw
sizeOfFixation = (int)f.Length / FIX_SIZE_COEFICIENT; //compute size of circle
annotationOverlay.FillEllipse(dotBrush, f.PosX - 1, f.PosY - 1, 3, 3);
//draw fixation on overlay
annotationOverlay.FillEllipse(brush, (f.PosX - sizeOfFixation), (f.PosY - sizeOfFixation), sizeOfFixation * 2, sizeOfFixation * 2);
}
//eventHandler for mouseMove - this method color rectangle over which mouse hover to red border
private void HighlightAOI(object sender, EventArgs e)
{
//this just draw last highlighted rect to black when we not yet hover mouse above it
if (lastHighlightedAOI != null)
{
annotationOverlay.DrawRectangle(new Pen(Color.Black, 1), lastHighlightedAOI.Start.X, lastHighlightedAOI.Start.Y, (lastHighlightedAOI.End.X - lastHighlightedAOI.Start.X), (lastHighlightedAOI.End.Y - lastHighlightedAOI.Start.Y));
}
//get position of mouse sursor
var x = pictureBox.PointToClient(Cursor.Position).X;
var y = pictureBox.PointToClient(Cursor.Position).Y;
var tempFix = new Fixation() { PosX = x, PosY = y };
//get rectangle over which mouse hover
lastHighlightedAOI = tempFix.LiesIn(annotationAOIs).FirstOrDefault();
if (lastHighlightedAOI != null)
{
//highlight rectangle by painting red border
annotationOverlay.DrawRectangle(new Pen(Color.Red, 1), lastHighlightedAOI.Start.X, lastHighlightedAOI.Start.Y, (lastHighlightedAOI.End.X - lastHighlightedAOI.Start.X), (lastHighlightedAOI.End.Y - lastHighlightedAOI.Start.Y));
}
}
//eventHandler for mouse click
private void SelectAOI(object sender, EventArgs e)
{
//get position of cursor
var x = MousePosition.X;
var y = MousePosition.Y;
var tempFix = new Fixation() { PosX = x, PosY = y };
//get rectangle which we selected by a mouse click
var aoi = tempFix.LiesIn(annotationAOIs).FirstOrDefault();
//assign last shown fixation to selected rectangle
if (aoi != null)
{
aoi.AssignedFixations.Add(Fixations[fixationCounter - 1]);
}
//if it wasn't last fixation again call ExpertAnnotation function to draw another Fixation over image (!!! here I need to clear last drawn fixation (circle) disappear and draw next fixation in ExpertAnnotate method)
if (fixationCounter != Fixations.Count)
{
ExpertAnnotate(sender, e);
}
else
{
TerminateExpertAnnotation("regular");
}
}
Thanks to #Reza Aghaei who guided me in chat to solution.
For me acceptable solution was in building multilayer image and assigning it to pictureBox.Image attribute.
I built image by loading image from file:
Image im = new Bitmap(path); // loads image from file
Then create graphics from this image:
var g = Graphics.FromImage(im); // creates graphics from loaded image
Draw all needed rectangles to this image and backup this image to some global Image instance:
var pen = new Pen(Color.Black, 1);
// draws all rectangles on the image
annotationAOIs.ForEach(a =>
{
g.DrawRectangle(pen, a.Start.X, a.Start.Y, (a.End.X - a.Start.X), (a.End.Y - a.Start.Y));
});
g.Dispose(); // disposes used graphics
ImageBackup = new Bitmap(im); // backup image with rectangles
In above part I created a static part of an image, which will not change and I backed it up so next time I will just create new Image instance from backup without any rectangle drawing.
Then when I want to show up new circle over this image I just:
var image = new Bitmap(ImageBackup); // creates new instance of image with rectangles from backup
var g = Graphics.FromImage(image); // creates graphics from image
// in this part draw circle at specific point
var f = Fixations[fixationIndex];
sizeOfFixation = (int)f.Length / FIX_SIZE_COEFICIENT;
g.FillEllipse(dotBrush, f.PosX - 1, f.PosY - 1, 3, 3);
g.FillEllipse(brush, (f.PosX - sizeOfFixation), (f.PosY - sizeOfFixation), sizeOfFixation * 2, sizeOfFixation * 2);
pictureBox.Image.Dispose(); // dispose old pictureBox image
pictureBox.Image = image; // set new image
imageOverlay = pictureBox.CreateGraphics(); // get transparent graphics overlay for pictureBox so we can draw anything else over picture (in my case highlighting rectangles over which I hover a mouse)
g.Dispose(); // dispose used graphics
Your best approach would be to use the OnPaint event handler for the PictureBox control and place all your drawing calls in there.
You can use the Graphics object passed to the event handler to get the surface you want to draw on (i.e. the Picture box) and then use the various methods to draw the shapes that you are after.
To draw a 'transparent' shape simply draw an outline rather than a filled shape.

Put a shape on pictureBox, c#

I have a pictureBox, I need to put some ovalshapes on different places over the pictureBox..
In some reason the shapes are behind the picture and they unseen..
Can I do something that the shapes will be over the picture and not behind it..?
What property change this?
To paint some oval shapes you use a List<Rectangle> ovals = List<Rectangle>();
To draw them you have a choice of
drawing them into the Image of the PictureBox or--
on top of the PictureBox.
For the former you need to create the Graphics object from the Image you want to change:
Bitmap bmp = new Bitmap(pictureBox1.Image);
using (Graphics graphics = Graphics.FromImage(bmp))
foreach (Rectangle R in ovals)
graphics.FillEllipse(yourBrush, R.X, R.Y, R.Width, R.Height);
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
pictureBox1.Image = bmp;
This code will change the pixels and can be put anywhere.
For the latter you use the Paint event:
foreach (Rectangle R in ovals)
e.Graphics.FillEllipse(yourBrush, R.X, R.Y, R.Width, R.Height);
This version will only change the surface of the PicureBox and needs to be put in or be triggered from the Paint event.
I think you want this version..

Adding a picture box behind a Transparent Panel

I have two panels on top of each other. The one below is a bit larger than the one on top. I am painting an image by using CreateGraphics() method on the top most panel. (To be clear this image is a connect four grid with transparent holes). Now what I need to do is to add a picture box to the bottom panel and have it show from behind this grid.
I am adding controls of picture box to the bottom grid. And I'm using the BringToFront() method too. What can I do to have the picture box show underneath the grid?
In the following code: chipHolder is the bottom panel, grid is the topmost panel and picBox is the picture box respectively
public void addControl()
{
chipHolder.Controls.Add(picBox);
picBox.BringToFront();
}
// This piece of code is in a mouse_click event of grid
Graphics g = grid.CreateGraphics();
addControl();
// to make the picture move downwards
for (int i = 0; i < newYloc; i++)
{
picBox.Location = new Point(newXloc, picBox.Top + 1);
picBox.Show();
}
// drawing the grid image on the grid panel
protected virtual void grid_Paint(object sender, PaintEventArgs e)
{
Image img = Properties.Resources.grid_fw;
gridGraphics = grid.CreateGraphics();
gridGraphics.DrawImage(img, 0, 0, 650, 550);
}
To have a better picture this is how my panels are. the one selected is the chipHolder panel.
You could try a different approach: don't use a Panel and use a single PictureBox, this way you draw everything in that PictureBox. Thus, you use PictureBox's MouseDown event handler to calculate the (virtual) cell the user has clicked (you need to perform a simple division) and then you draw the chip on the PictureBox. If you want to show the chip falling, you'd need to save a copy of the current Bitmap (the Image property of the PictureBox) and draw the chip on different y coordinates (from 0 to its final position on the grid), this would be just like the double-buffer technique.
Here is a small example (you need a Form with a PictureBox, in this example it's named "pictureBox2"):
public partial class Form1 : Form
{
Bitmap chip = new Bitmap(40, 40, PixelFormat.Format32bppArgb);
public Form1()
{
InitializeComponent();
using (Graphics g = Graphics.FromImage(chip))
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.FillEllipse(new SolidBrush(Color.FromArgb(128, 255, 80, 0)), 1, 1, 38, 38);
}
pictureBox2.Image = new Bitmap(pictureBox2.Width, pictureBox2.Height, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(pictureBox2.Image))
{
g.Clear(Color.Yellow);
}
}
private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Text = e.Location.ToString();
using (Graphics g = Graphics.FromImage(pictureBox2.Image))
{
g.DrawImage(chip, e.Location.X - 20, e.Location.Y - 20);
}
pictureBox2.Invalidate();
}
}
}
If you want controls with real transparency, you should use WPF (it provides better graphics and uses hardware acceleration).

Drawing an image in this method?

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;

Categories