C# Draw on a PictureBox - c#

Suppose i have an PictureBox with image assigned to it, and i just want to draw a smaller bitmap above it.
This is my code:
Bitmap a = Getimage();//just a small function generates a new image.
Bitmap f = (Bitmap) pictureBox1.Image;//getting the picturebox image.
Graphics g = Graphics.FromImage(f);
g.DrawImage(a,150,200);//draws a on f at (150,200).
PictureBox1.Image=f;
However, in my program it runs on a loop in a seperated thread, so im getting an Error:
Object is in use elsewhere
Is there any way to draw directly to the Picturebox itself? instead of getting it's bitmap and draw, and assign again? Or at least how may i solve the above exception?
Thanks.

this will help you
public Form1()
{
InitializeComponent();
new Thread(MyDraw).Start();
}
private void MyDraw()
{
while(true)
{
Invoke(new Action(DrawItem));
Thread.Sleep(1000);
}
}
private void DrawItem()
{
using (var graphics = Graphics.FromImage(pictureBox1.Image))
{
graphics.DrawString("Hello", new Font("Arial", 20), Brushes.Yellow, PointF.Empty);
}
}

Related

Draw directly to PictureBox

I'm developing a screen-sharing app which runs a loop constantly and receives small frames from a socket. The next step is to draw them into the PictureBox.
Of course, I use thread because I don't want to freeze the ui.
This is my code:
Bitmap frame = byteArrayToImage(buff) as Bitmap;//a praticular bitmap im getting from a socket.
Bitmap current = (Bitmap)pictureBox1.Image;
var graphics = Graphics.FromImage(current);
graphics.DrawImage(frame, left, top);//left and top are two int variables of course.
pictureBox1.Image = current;
But now I'm getting an error:
Object is already in use elsewhere.
in this line var graphics = Graphics.FromImage(current);
Tried to Clone it, Create a New Bitmap(current). still no success.
Invalidate() your PictureBox so it redraws itself:
Bitmap frame = byteArrayToImage(buff) as Bitmap;
using (var graphics = Graphics.FromImage(pictureBox1.Image))
{
graphics.DrawImage(frame, left, top);
}
pictureBox1.Invalidate();
If you need it to be thread safe, then:
pictureBox1.Invoke((MethodInvoker)delegate {
Bitmap frame = byteArrayToImage(buff) as Bitmap;
using (var graphics = Graphics.FromImage(pictureBox1.Image))
{
graphics.DrawImage(frame, left, top);
}
pictureBox1.Invalidate();
});

Draw to PictureBox from other function

I am trying to draw to a PictureBox from another function which is not the paint event!
Here is what it looks like:
public void Draw(ref PictureBox picture_box)
{
Graphics graphics = picture_box.CreateGraphics();
for (int i = 0; i < ship.Count; ++i)
{
if (ship[i].form == FORM.RECTANGLE)
{
graphics.FillRectangle(ship[i].color, ship[i].rectangle);
}
else if (ship[i].form == FORM.ELLIPSE)
{
graphics.FillEllipse(ship[i].color, ship[i].rectangle);
}
}
graphics.Dispose();
picture_box.Refresh();
}
(ship is a List of ship_part classes, which contain a Rectangle, a FORM enum (RECTANGLE, ELLIPSE) and a Brush)
I call Draw in the Form's constructor :
public Form1()
{
InitializeComponent();
spaceship ship;
ship_part part;
ship = new spaceship();
part = new ship_part(); //constructor initializes form to RECTANGLE and color to Brushes.Black
part.rectangle = new Rectangle(50, 50, 10, 10);
ship.ship.Add(part);
ship.Draw(ref pictureBox1);
}
The problem is that the Rectangle is not drawn.
What I have tried so far :
I thought there might be a problem with this line :
Graphics graphics = picture_box.CreateGraphics();
I think that I am not writing to the actual Graphics object that belongs to the PictureBox so I made my Draw function return the Graphics object that gets modified. Then I called the Draw function from the Picture Box Paint Event and tried to assign that Graphics object to the one that gets passed as parameter in the PaintEventArgs. But the PaintEvenArgs.Graphics object is read only.
Thanks!

Preview screen shot image in a winform

Im in the process of making a snipping tool. I've got it so that my program can be used to draw a rectangle with the mouse and have that image saved. Now what I'm trying to do is have the image generated be transfered to a picture box that show's the user what they have just captured before they decide to save it or anything else.
Is there a way in which I can do this?
So far my screen capture code saves the captured image to the clipboard with the following code found in my ScreenCapture class:
public static bool saveToClipboard = true;
public static void CaptureImage(bool showCursor, Size curSize, Point curPos, Point SourcePoint, Point DestinationPoint, Rectangle SelectionRectangle, string FilePath, string extension)
{
using (Bitmap bitmap = new Bitmap(SelectionRectangle.Width, SelectionRectangle.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(SourcePoint, DestinationPoint, SelectionRectangle.Size);
if (showCursor)
{
Rectangle cursorBounds = new Rectangle(curPos, curSize);
Cursors.Default.Draw(g, cursorBounds);
}
}
if (saveToClipboard)
{
Image img = (Image)bitmap;
Clipboard.SetImage(img);
}
}
}
Has anyone ever done something like this before? Also, is it possible to have the picture box auto resize so that the screen capture size is used and not the picture boxes?
update
Further to some of the comments below, I've been trying to save the image I store in my above class and pass it to the picture box. But nothing is displayed when I do it. The code I've been using is thus:
Held on the form1.cs:
public void SetImage(Image img)
{
imagePreview.Image = img;
}
And in the screen capture class:
if (saveToClipboard)
{
Image img = (Image)bitmap;
ControlPanel cp = new ControlPanel();
cp.SetImage(img);
Clipboard.SetImage(img);
}
ControlPanel cp = new ControlPanel();
cp.SetImage(img);
this won't work because you need to access the parent form in use, not create a new instance of it.
Look at the answer to this question on creating a simple custom event, but add an Image to the ProgressEventArgs that they create. Then on your main form, subsribe to the event and update the picturebox from there.

Merging graphics objects does not render text correctly

I'm trying to solve a problem with a custom control ported from a VCL C++ application. The idea is that individual parts of the control are drawn first on a new Graphics object and then merged with the Graphics object from the control's paint method.
I've created a simplified example:
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class Form1 : Form
{
private PictureBox pictureBox;
public Form1()
{
pictureBox = new PictureBox();
pictureBox.Dock = DockStyle.Fill;
pictureBox.Paint += new PaintEventHandler(pictureBox_Paint);
ClientSize = new Size(100, 50);
Controls.Add(pictureBox);
}
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
SolidBrush blackBrush = new SolidBrush(Color.Black);
Bitmap bitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
Graphics graphics = Graphics.FromImage(bitmap);
Font font = new Font(pictureBox.Font, FontStyle.Regular);
graphics.DrawString("simple test", font, Brushes.Black, 0, 0);
e.Graphics.DrawImage(bitmap, 0, 0);
}
}
}
Running the above code results in the text being drawn too thick:
When i modify the code to draw the text directly to the Graphics object of the PictureBox i get the correct result:
This problem only occurs with text rendering. Lines and other shapes are rendered correctly. Any ideas how to solve this?
Thanks in advance.
This happens because you forgot to initialize the bitmap pixels. By default they are Color.Transparent, which is black with an alpha of 0. This goes wrong when you draw anti-aliased text into the bitmap, the aliasing algorithm will draw pixels that blend from the foreground (black) to the background (also black). Blobby letters that are not anti-aliased is the result.
This is not a problem in the 2nd screenshot because the background pixels were drawn to battleship gray by the default Form.OnPaintBackground() method. Simply add the Graphics.Clear() method to fix your problem:
using (var bitmap = new Bitmap(pictureBox.Width, pictureBox.Height)) {
using (var graphics = Graphics.FromImage(bitmap)) {
graphics.Clear(this.BackColor); // <== NOTE: added
graphics.DrawString("simple test", pictureBox1.Font, Brushes.Black, 0, 0);
}
e.Graphics.DrawImage(bitmap, 0, 0);
}
With using statements added to prevent your program from crashing when the garbage collector doesn't run often enough.

C# Threading Bitmap objects/PictureBox

I have some code to display video-like graphics of points moving around.
I am writing the points to a bitmap, and placing it on a picturebox.
The graphics computation has to be done on its own thread. The graphics work fine as long as you don't move the window around "too" much.
I'm using winforms. When I run the code, and move the window around wildly, I SOMETIMES get the following errors:
# this.Invoke(d, new object[ ] { bmp
}); "Cannot access a disposed
object. Object name: 'Form1'."
# gfx.DrawImage(bmpDestination, new
Point()); "Object is currently in
use elsewhere."
Here is the code:
private void button2_Click(object sender, EventArgs e)
{
Thread demoThread = new Thread(new ThreadStart(ThreadProcSafe));
demoThread.Start();
}
private void ThreadProcSafe()
{
creategraphics();
}
private void creategraphics()
{
Bitmap bmpDestination = new Bitmap(988, 588);
Bitmap bmp = new Bitmap(988, 588);
for (int i = 0; i < numtimesteps; i++)
{
bmp = GraphingUtility.create(apple, i, 988, 588, -30, 30, -30, 30);
using (Graphics gfx = Graphics.FromImage(bmp))
{
gfx.DrawImage(bmpDestination, new Point());
}
bmpDestination = bmp;
updateimage(bmp);
}
}
delegate void graphicscallback(Bitmap bmp);
private void updateimage(Bitmap bmp)
{
if (pictureBox1.InvokeRequired)
{
graphicscallback d = new graphicscallback(updateimage);
this.Invoke(d, new object[] { bmp });
}
else
{
pictureBox1.Image = bmp;
pictureBox1.Refresh();
}
}
There are problems using GDI objects across threads.
Look at Hans' double-clone suggestions in this thread.
Cannot access a disposed object. Object name: 'Form1
You get this because you don't do anything to stop the thread when the user closes the form. You'll have to keep the form alive until you know that the thread is dead. Pretty hard to do reliable, check this answer for a solution.
As an aside, calling InvokeRequired is an anti-pattern. You know that you are making the call from a worker thread. If InvokeRequired would return false then there's something really wrong. Don't bother, call Invoke() directly.
Object is currently in use elsewhere
It is an exception raised by GDI+ (Graphics) when it sees that two threads are trying to access a bitmap at the same time. It isn't obvious in your snippet but I can't see what GraphingUtility.create() does. Make sure it creates a new bitmap, not return an existing one. Because that will bomb when your thread writes to it again and the picture box repaints itself at the same time. The bmp constructor you use above it doesn't do anything.
Your PictureBox.Image assignment is forgetting to dispose the old one.

Categories