My problem is:
System.ComponentModel.Win32Exception: 'Error creating window handle'.
I know I can solve this problem with Dispose(), but when I use it in the program, I'm displaying another error:
System.ObjectDisposedException: 'Can not access a disposed object. Object name: 'PictureBox'. '
I use the following code:
private void SetUpPuzzle_Click(int parts)
{
Panel P = new Panel
{
Size = new Size(200, 200),
Location = new Point(394, 62),
};
Controls.Add(P);
Control board = P;
int total = parts * parts;
var PB = new PictureBox[total];
var imgarray = new Image[total];
var img = User_Image.Image;
int W = img.Width / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
int H = img.Height / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
int size = 200 / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
for (int x = 0; x < parts; x++)
{
for (int y = 0; y < parts; y++)
{
var index = x * parts + y;
imgarray[index] = new Bitmap(W, H);
using (Graphics graphics = Graphics.FromImage(imgarray[index]))
graphics.DrawImage(img, new Rectangle(0, 0, W, H),
new Rectangle(x * W, y * H, W, H), GraphicsUnit.Pixel);
PB[index] = new PictureBox
{
Name = "P" + index,
Size = new Size(size, size),
Location = new Point(x * size, y * size),
Image = imgarray[index],
SizeMode = PictureBoxSizeMode.StretchImage
};
PB[index].MouseEnter += Images_M_E;
PB[index].MouseLeave += Images_M_L;
PB[index].MouseClick += Form_MouseClick;
*PB[index].Dispose();
*board.Controls.Add(PB[index]);
}
}
}
When I want to create 10,000 objects
This error is displayed.
My problem is:
Indeed. You are creating way too many controls for a
Winformsapplication.And disposing of them doesn't really help because you can't use a disposed object any longer..
To have this kind of large puzzle (10k pieces) you need to change from using
PictureBoxes(or any otherControls) to display the puzzle pieces to a different approach. This has been suggested in the original question but then you only wanted to have 100 pieces, remember?The most common approach is this: Keep a list of images (when they are <= 256x256 pixels do put them into an
ImageList!) and draw them in the board'sPaintevent. This will get rid of all the overhead involved withPictureBoxes.(Aside: One may think this will not be performant with all the
DrawImagecalls. But all thosePictureBoxesalso need to draw all the pixels on all their surfaces, so that is no issue. But they also have to carry the overhead of being (under the hood) fully functionalwindows(see the error message!), which is why the system can only have a limited number of them; always try to keep the number of controls < 1k!)You will have to move the placement logic to the board's
Paintevent and will also have to change the event model..:Instead of having each
PictureBoxrespond to its own events you will have to find a way to do all the work in the board's events. This will have to be diffenrent, depending on the event.Since we don't know which event you have and what they do and which data they need for their work, it is hard to give all the necessary details, so I'll just point out a few things..:
There will not be a
EnterorLeaveevent you can use. Instead you need to detect entering an area of a piece by testing for it in the MouseMove event. If you keep aList<Rectangle>you can useRectangle.Contains(e.Location)for this test.You can detect a MouseClick but then will have to find out which area was clicked. If your Enter and Leave logic from the MouseMove is working you can use its result to know where the Click went.
Similar ideas can be used for all other events; some are simple, some need a little calculation but they will all be fast and pretty easy to implement..
To optimize performance try to make the image n the right size and use Format32bppPArgb as the pixel format, because it is faster to display.
Another option is to pull the pixel data right from the original image in the
Paintevent with the same calculations you use now to create them. (There is aDrawImageoverlay that uses twoRectangles, one to determine the target and one for the source area..) This savesGDIhandles, at least if you can't use anImageList.Always plan for growth! For a better implementation do create a
Piececlass. It should hold aRectangleand an integer index into theImageList'sImagescollection. It could also have a methodSwitch(Piece otherPiece)which would either switch theRectanglesor the indices.Good luck :-)