handler is invalid in a BitmapEncoder.Frames.Add call

250 Views Asked by At

I'm using this custom method to convert a Bitmapsource to a Bitmap:

    public Bitmap BitmapFromSource(BitmapSource bitmapsource) {
        using (MemoryStream outStream = new MemoryStream()) {
            BitmapEncoder enc = new BmpBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(bitmapsource));
            enc.Save(outStream);
            using (var tempBitmap = new Bitmap(outStream)) {
                return new Bitmap(tempBitmap);
            } } }

this method is called several times in my code and it works great. But when I'm calling it with a mouse click event or a key down event, i get the handler is invalid error.

I don't have any clue on how to proceed. any suggestion?

In the code I have a list of bitframes:

List<BitmapFrame> myImages = new List<BitmapFrame>();

initially the method works when I show each frame in to several pictureboxes:

var handler = new EventHandler(pictureBoxClick);

for (int i = 0; i < (decoder.Frames.Count); i++) {
   var picture = new MyPictureBox {
    Name = "pictureBoxFrame" + i,
    Width = (int)newWidth,
    Height = (int)newHeight,
    SizeMode = PictureBoxSizeMode.Zoom,
    Location= loc,
    Image = BitmapFromSource(myImages[i]), };
Controls[i].Click += handler;
splitContainer1.Panel1.Controls.Add(picture);
}

then when I click one of these pictureboxes:

void pictureBoxClick (object sender , EventArgs e) {
    var selectedPictureBox = (PictureBox)sender;
    int idx = Controls.IndexOf(selectedPictureBox);
    pictureBox1.Image = BitmapFromSource(myImages[idx]);
}

I get the error...

1

There are 1 best solutions below

9
Jimi On

From what I can see from your code, you are quite probably a victim of the "Stream".
Meaning that, when you are loading a resource from a FileStream, the stream must be available (not disposed) for the lifespan of that resource.
This applies to the image resource referenced by a BitmapDecoder, too.
If you dispose the stream used to initialize the BitmapDecoder, it's Frames collection will be set to 1 frame of 1x1 size.
Since leaving the stream open is not a good idea, it is usually copied to a MemoryStream (the raw byte array) and disposed right after.

No matter what you might have read or heard, a MemoryStream does not really need to be disposed (not immediately, at least), because it doesn't really use any vital resources.

Having loaded an image resource from a stream, the BitmapDecoder can be initialized this way:

MemoryStream memstream = new MemoryStream();
stream.CopyTo(memstream);
memstream.Position = 0;
BitmapDecoder bitmapDecoder = BitmapDecoder.Create(memstream,
                                            BitmapCreateOptions.PreservePixelFormat,
                                            BitmapCacheOption.Default);

This way, the BitmapFrames of the BitmapDecoder will have a valid handle, because the underlying stream is still alive.
When you load a new image, the MemoryStream will be updated using a new FileStream.

To test the result of what has been written here and in the comments (those suggestions are relevant too), I've built a Test Form class.
You can download the source code from PasteBin.