3
votes

I have a WriteableBitmap in a Windows Phone 8 application that is hooked up to an Image control. I'm looping through each row of the image and painting a row of pixels at a time asynchronously and then scheduling the next row for painting. However, it appears that changing the underlying pixel data does not fire a property changed so the control is not being updated. If I set the image source to a new WriteableBitmap created from the same pixels, the image updates fine but I'm doing a lot of excessive array copying.

void PaintImage(object state)
{
    // get my height, width, row, etc. from the state
    int[] bitmapData = new int[width];
    // load the data for the row into the bitmap

    Dispatcher.BeginInvoke(() =>
    {
        var bitmap = ImagePanel.Source as WriteableBitmap;
        Array.Copy(bitmapData, 0, bitmap.Pixels, row * width, bitmapData.Length);

        if (row < height - 1)
        {
            var newState = ... // create new state
            ThreadPool.QueueUserWorkItem(PaintImage, newState);
        }
    });
}

If I add these lines after the Array.Copy above, the bitmap progressively is drawn to screen (although in reality it is just replacing the bitmap every time):

var newBitmap = new WriteableBitmap(width, height);
Array.Copy(bitmap.Pixels, newBitmap.Pixels, newBitmap.Pixels.Length);
ImagePanel.Source = newBitmap;

It seems like I need to manually have the WriteableBitmap fire some property changed notification so that the Image that has it. I'm guessing this problem will go away if I bind the image to a WriteableBitmap in a ViewModel?

2
Can't you wait for the whole picture to finish drawing and then replace the whole imageSource with the new updated picture? - robertk
No, that's not the way I want it to function. - sohum
Then the easiest way is probably bind it to a VM and do a RaisePropertyChanged() everytime you want the picture to refresh. - robertk
Yeah, I mentioned that in the original question and in fact got it working with a VM. I was curious whether it was possible for one to manually refresh a control and have it repaint based on the new data. - sohum

2 Answers

3
votes

Just add a dirty rectangle

 _myBitmap.Lock();
 _myBitmap.AddDirtyRect(new Int32Rect(0, 0, _myBitmap.PixelWidth, _myBitmap.PixelHeight));
 _myBitmap.Unlock();

or if you are on a background thread

 Application.Current.Dispatcher.InvokeAsync(() =>
    {
        _myBitmap.Lock();
        _myBitmap.AddDirtyRect(new Int32Rect(0, 0, _myBitmap.PixelWidth, _myBitmap.PixelHeight));
        _myBitmap.Unlock();
    });