I am putting together an application that uses more than one picturebox on the GUI. I have written custom events for each picturebox that are called on paint events.
Unfortunately, when the application runs, precisely one picturebox displays properly. The others simply display as white boxes. Whichever opicturebox happens to be added first in form.designer is the picturebox that displays properly, for the others their paint events are never triggered. I have tried to get around this by calling various combinations of picturebox.refresh(), picturebox.update() and picturebox.invalidate() in the form constructor, to no avail.
Interestingly, when I associate a button press event with picturebox.refresh() and picturebox.update() and press the button once the program is running, the pictureboxes start behaving as normal - the paint event is called and the image updates.
Does anyone have any ideas how I can get all of the pictureboxes to display properly on initialisation?
Code snippets that may be useful:
Paint code for the pictureboxes:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
paintPictureBox(e, PictureBox1, currentImage[0]);
}
private void paintPictureBox(PaintEventArgs e, PictureBox picBox,ImageObject c)
{
try
{
Font myFont = new Font("Arial", 14);
//Get bitmap
Bitmap imageToDisplay = new Bitmap(Bitmap.FromFile(c.ImageFile));
//resize smallest dimension to 200
if (imageToDisplay.Height > imageToDisplay.Width)
{
imageToDisplay = new Bitmap(Bitmap.FromFile(c.ImageFile), 200, 200 * imageToDisplay.Height / imageToDisplay.Width);
}
else
{
imageToDisplay = new Bitmap(Bitmap.FromFile(c.ImageFile), 200 * imageToDisplay.Width / imageToDisplay.Height, 200);
}
//crop anything outside of 200x200
imageToDisplay = imageToDisplay.Clone(new Rectangle(Math.Max((imageToDisplay.Width - 200) / 2, 0), Math.Max((imageToDisplay.Height - 200) / 2, 0), 200, 200), imageToDisplay.PixelFormat);
//now draw it
picBox.Image = imageToDisplay;
//add the name
e.Graphics.DrawString(c.Name, myFont, Brushes.Maroon, new Point(2, 200));
}
catch (Exception E)
{
MessageBox.Show("Could not display image successfully\n" + E.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Code from form.designer:
//This one displays correctly on initialisation
this.Controls.Add(this.PictureBox0);
//These two do not
this.Controls.Add(this.PictureBox1);
this.Controls.Add(this.PictureBox2);
((System.ComponentModel.ISupportInitialize)(this.PictureBox0)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.PictureBox1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.PictureBox2)).EndInit();
Code for button press that makes pictureboxes display correctly (after initialisation):
private void Button_Click(object sender, EventArgs e)
{
//Refresh the picture boxes
foreach (PictureBox pb in this.Controls.OfType<PictureBox>())
{
pb.Refresh();
pb.Update();
}
}
The Paint event is raised every time your window needs to be repainted. For example, if another window is being dragged over the top of yours, the Paint event will be raised several times per second. Hence it is important that your paintPictureBox function does nothing but paint.
The problem is that paintPictureBox is continually reinitialising the bitmap but not actually drawing it. Additionally, you are reading from the disk and creating several bitmaps every time you paint. This is slow and uses gobs of memory.
Instead, you should initialise each picture box's Image property once, when the form loads. Each picture box will then take care of drawing its own bitmap. Your paint event should do nothing but draw the picture name.
Related
I have an issue with printing from a panel (C#, windows form). I created a Print button, I wrote the necessary codes for that and it seems that only one textboxes are able to shown as it is supposed to be. The rest of the controls, such as the richtextboxes are blank, even if I write into them.
Bitmap bitmap;
private void btn_Print_Click(object sender, EventArgs e)
{
Print(this.panel_Doc_Print);
}
public void Print(Panel panel)
{
PrinterSettings set = new PrinterSettings();
panel_Doc_Print = panel;
PrintArea(panel);
print.Document = print_doc;
print_doc.PrintPage += new PrintPageEventHandler(PrintImage);
print.ShowDialog();
}
public void PrintImage(object sender, PrintPageEventArgs e)
{
Rectangle page_area = e.PageBounds;
e.Graphics.DrawImage(bitmap, (page_area.Width / 2) - (this.panel_Doc_Print.Width / 2), this.panel_Doc_Print.Location.Y);
}
public void PrintArea(Panel panel)
{
bitmap = new Bitmap(panel.Width, panel.Height);
panel.DrawToBitmap(bitmap, new Rectangle(0, 0, panel.Width, panel.Height));
}
Before the printing the Windows form looks like this:
Windows form before printing.
Before I click on the print button:
before
After I click on the print button the following happens:
after
What did I do wrong? When I click on the button I want the program to print the whole panel, including all items and control in it. It happens only to the textboxes. As you can see on the 3rd picture the size of the printing image is quite small. I want it to roughly fill out an A4 sheet of paper.
At this point I am too confused to solve this problem alone, therefore I would like to ask for your help.
I am developing the GUI in C# using picturebox, button, and check box.I need to plot two different plots in parallel using one text file on an image. The image first reads in a picture box. The rest of the program has to be executed in the following way:
Reads text file contains the data points which have to be plotted.
After clicking the button the program execution started with the plotting of the First graphic (rectangular dot), however when the check box is checked the second graphic (continuous dots--path plot) plotting started in parallel with the first graphic.
when the check box is unchecked the second graphic stopped plotting and disappears.(As Both of the graphics style using the same text file).
I need help what to do in this case, should I create the separate thread for check box for this parallel plotting??
please help me where I am mistaken? And pardon my horrible English
From my point of view, the easiest way would be to reload the image into the picture box again and then redraw the first graphics object on it. So, the second one 'disappears'.
An additional thread makes no sense, because drawing must occur on the UI thread only (Windows general rule for GDI+, WinForms, WPF).
Such basic drawing as in your example is very fast.
Edit:
namespace PictureBoxDrawing
{
public partial class Form1 : Form
{
private Bitmap _bmpImage;
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_bmpImage = new Bitmap(#"C:\Image.jpg");
InitializePictureBox(_bmpImage);
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
DrawPictureBox(pictureBox1, 10, 10, checkBox1.Checked);
}
private void button1_Click(object sender, EventArgs e)
{
DrawPictureBox(pictureBox1, 10, 10, checkBox1.Checked);
}
private void button2_Click(object sender, EventArgs e)
{
InitializePictureBox(_bmpImage);
}
private void DrawPictureBox(PictureBox pb, int x, int y, bool drawBlue)
{
using (Graphics g = pb.CreateGraphics())
{
g.FillRectangle(Brushes.Red, x,y, 9,9);
if(drawBlue)
g.FillRectangle(Brushes.Blue, x, y, 7, 7);
}
}
private void InitializePictureBox(Bitmap bmp)
{
pictureBox1.Image = bmp;
}
}
}
From your example, here is my simplified suggestion. Load and cache the bitmap in a field for later use. I have added a second button2, which can be used to reload the image into the picture box to demonstrate the behaviour. Because the red rectangle is greater than the blue (9 > 7), it overwrites it when redrawing. So it is not necessary to reload the bitmap, if the position is constant. If the position changes, call InitializePictureBox prior to the DrawPictureBox call.
I'm having a problem with refreshing graphics on panels and forms.
When I draw the image it works fine but when I want to replace it with another image using Panel.Refresh it makes the event handler auto activate itself without end. If I use Panel.Update it will just draw the second image onto the first. A lot of people recommended that I use the Invalidate method but that has the same infinite loop problem.
Bitmap bitmap = new Bitmap(Resources.Image1);
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (parameter == 0) { bitmap = new Bitmap(Resources.Image1); }
if (parameter >= 2) { bitmap = new Bitmap(Resources.Image2); }
e.Graphics.DrawImage(bitmap, 60, 10);
panel1.Refresh();
}
panel1.Refresh();
Causes the paint event to trigger. I put the refresh code where the parameter value is changed.
i am creating a drawing programe which Suppose to take the parameters from the user
(Radius,height,width,....etc)
i have created a class with one paramter (radius)
public int faceoutline(int r)
{
Graphic = pictureBox1.CreateGraphics();
Graphic.DrawEllipse(myPen, 0, 0, r, r);
return r;
}
then i call it :
private void button1_Click(object sender, EventArgs e)
{
faceoutline(int.Parse(textBox1.Text));
pictureBox1.Invalidate();
}
....and nothing happens
i copied the button code to a timer but it keep balnking
WHAT I AM DOING WRONG ?!
You should place the drawing code in the Paint event handler of the PictureBox, and use the Graphics provided in the event args. This way, your custom drawing code will be executed every time the control is redrawn.
If you don't do it this way, anything you draw will disappear the next time the control is redrawn. In your code, you call Invalidate right after you draw your ellipse, so the control is redrawn without the ellipse...
Relatively new to C#; hopefully I'm just overlooking something simple.
I have a form named 'Exercise1' which contains a picture box called 'drawingArea' and a few buttons. The code for the constructor of Exercise1 is as follows:
public Exercise1()
{
InitializeComponent();
paper = drawingArea.CreateGraphics();
balloon = new Balloon("redBalloon", Color.Red, drawingArea.Width / 2,
drawingArea.Height / 2, 30);
paper.Clear(Color.White);
balloon.Display(paper);
}
...
'paper' and 'balloon' are created as globals above the constructor for use in the other methods on the form. Both 'paper' and 'balloon' work as initialized in the constructor in the other methods defined on the form.
For whatever reason, the commands
paper.Clear(Color.White);
and
balloon.Display(paper);
Which should clear the picture box and show a red ellipse, don't execute (at least visibly). What gives?
UPDATE:
Think I'm going to like this website... You guys are quick!
#Nitesh: The constructor for Exercise1 is called from another form. Code is as follows:
private void button1_Click(object sender, EventArgs e)
{
int exSelector = (int)numericUpDown1.Value;
switch (exSelector)
{
case 1:
Exercise1 form1 = new Exercise1();
form1.Show();
break;
...
#Sean Dunford: Yes and yes it is.
#RBarryYoung: Was playing around with that a bit, but had no luck. What command triggers a Form_Load event for Exercise1?
UPDATE: This altered code works as expected:
public Exercise1()
{
InitializeComponent();
paper = drawingArea.CreateGraphics();
drawingArea.BackColor = Color.White;
drawingArea.Paint += new PaintEventHandler(this.drawingArea_Paint);
balloon = new Balloon("redBalloon", Color.Red, drawingArea.Width / 2, drawingArea.Height / 2, 30);
}
private void drawingArea_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
balloon.Display(e.Graphics);
}
...
Thanks for all the help!
You cannot do drawing in the constructor. To do proper drawing, you need to have the form shown on the screen. You can try using the Shown event to do your rendering (this may get lost when the form is redrawn, though).
Usually the best way is to set whatever flags you need in the constructor and then use the Paint event of the form to do all painting. Later on, when you need to repaint something, set up whatever state needs to be rendered, invalidate your form (this results in a Paint event) and then you can repaint the new state.
If you try to do customized drawing (outside your Paint event) you'll run the risk of things randomly going blank or your drawing may disapper when you resize/minimize your form.
You use Graphics in a constructor, that means that you draw on the paper only once, any redraw for whatever reason that happens after constructor will draw the drawingArea in its original way. Try to add PaintEventHandler to drawingArea and then call inside balloon.Display(e.Graphics);
public Exercise1()
{
InitializeComponent();
balloon = new Balloon("redBalloon", Color.Red, drawingArea.Width / 2,
drawingArea.Height / 2, 30);
drawingArea.Paint += new PaintEventHandler(drawingArea_Paint);
}
void drawingArea_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
baloon.Display(e.Graphics);
}
You should be overriding the forms OnPaint event handler. In doing so, you are able to get the graphics context which will redraw your paper and balloon areas.