Do not dispose the image that is needed. Try this:
. . .
pictureBox1.Image?.Dispose( );
pictureBox1.Image = image;
//image.Dispose( ); REMOVED
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
I have a Windows Forms application with a number of dialog boxes that are opened when clicking on a menu item. I recently added a new DialogBox called Sierpinski whose OnClick event handler is as follows:
// Event handler for selecting the Sierpinski Triangle dialog box.
private void OnClickSierpinski (object sender, EventArgs e)
{
var sierpinskiDialog = new Sierpinski ();
SetTitleAndLocation (sierpinskiDialog, null);
sierpinskiDialog.ShowDialog (this);
}
The code for the DialogBox is shown below:
public partial class Sierpinski : Form
{
Bitmap image;
int x1 = 400, y1 = 10;
int x2 = 800, y2 = 440;
int x3 = 0, y3 = 440;
int xn = 400, yn = 10;
int plot;
Color color = new ();
Random random = new ();
public Sierpinski ()
{
InitializeComponent ();
DrawSierpinski ();
}
private void DrawSierpinski ()
{
image = new (800, 440, PixelFormat.Format24bppRgb);
for (int i = 1; i <= 10000; i++)
{
plot = random.Next (3) + 1;
switch (plot)
{
case 1:
xn = (xn + x1) / 2; yn = (yn + y1) / 2;
break;
case 2:
xn = (xn + x2) / 2; yn = (yn + y2) / 2;
break;
case 3:
xn = (xn + x3) / 2; yn = (yn + y3) / 2;
break;
}
color = Color.FromArgb (255 - i / 40, 0, i / 40);
image.SetPixel (xn, yn, color);
}
//pictureBox1.Image = image;
_ = Read ();
image.Dispose ();
}
Note that with the PictureBox line commented out, when I click the menu item the DialogBox is displayed, but of course, without an image. However, if I uncomment the PictureBox line, the DialogBox is not even displayed. Instead I get the following exception:
What's going on here? Why does adding the assignment of an image to the PictureBox.Image property prevent the DialogBox from displaying?
Do not dispose the image that is needed. Try this:
. . .
pictureBox1.Image?.Dispose( );
pictureBox1.Image = image;
//image.Dispose( ); REMOVED
There are several problems with your code.
Firstly you don't want to be doing that method call in the constructor. It'll mess everything up. Here's the problem. The constructor is called when you new
the instance. So the line var sierpinskiDialog = new Sierpinski ();
is what actually triggers a call to your Draw method. Notice that this is long before the UX is actually shown, if it is shown at all. The only thing you should be doing in your constructor is initializing the fields and setting up the control instances. Everything else should be deferred until the OnLoad
method is called. This method is called after the UX is created but just before it is shown (generally by ShowDialog
). It is at this point that you can do any final initialization of the UX before rendering.
Why does this matter? It matters because of the designer. When you load your form in the designer of VS it has to create an instance of your form. To do that it has to create an instance (just like you're doing in code) and that requires running the constructor. So anything in your constructor is run in the designer as well as runtime. Now, as you make changes to the UX in the designer it is, behind the scenes, writing those changes to the InitializeComponent
method. The designer is going to generate the same code that you would manually. The problem is that the designer cannot tell the difference between you making a change in the designer and your constructor calling methods that make changes. As far as the designer is concerned they are the same. So any methods you call in your constructor that adjust properties of the controls will ultimately get written to the InitializeComponent
method. Hence your method logic is duplicated. Now this happens whenever the designer regenerates the code and not necessarily right away, but it will eventually happen. So either configure your code in the designer (and let it right out the code) or defer any additional changes until OnLoad
so the designer doesn't generate the same code again.
The second problem with your code is the scope of the image. You define image
as a field. But you only use it in 1 method AND you cannot reuse that image because PictureBox
explicitly states that the image you give it cannot be used elsewhere. So you need to move the definition of image
into the method as a regular variable. This will prevent you from reusing it by accident.
Now we're to the third problem which is the lifetime of the image instance. You create the instance, set up the image and assign it to the PictureBox
. But then you dispose of the image which wipes it out. At the point you handed it to PictureBox
then PictureBox
is now responsible for the lifetime of the image. You cannot dispose of it because then you're disposing of the image that the control is trying to use. At that point it is going to crash.
Resolve these problems and see if the issue goes away. If it still remains then there could be something wrong with the image format but a cursory glance doesn't indicate any issues. Post your updated code and we can take another look.