views:

1164

answers:

3

Why I can't create CroppedBitmap in the following code ? I got an exception : The calling thread cannot access this object because a different thread owns it.

If I change the code to CroppedBitmap cb = new CroppedBitmap(new WriteableBitmap(bf), new Int32Rect(1, 1, 5, 5));

the exception is gone? why ?

Code 1: exception at cb.Freeze();

public MainWindow()
{
    InitializeComponent();

    ThreadPool.QueueUserWorkItem((o) =>
        {
            //load a large image file
            var bf = BitmapFrame.Create(
                new Uri("D:\\1172735642.jpg"),
                BitmapCreateOptions.None,
                BitmapCacheOption.None);
            bf.Freeze();
            Dispatcher.BeginInvoke(
                new Action(() =>
                    {
                        CroppedBitmap cb = new CroppedBitmap(bf, new Int32Rect(1,1,5,5));
                        cb.Freeze();
                        //set Image's source to cb....
                    }), 
                    System.Windows.Threading.DispatcherPriority.ApplicationIdle);
         }
    );
}

Code2: It works

            ThreadPool.QueueUserWorkItem((o) =>
            {
                var bf = BitmapFrame.Create(
                        new Uri("D:\\1172740755.jpg"),
                        BitmapCreateOptions.None,
                        //BitmapCreateOptions.DelayCreation,
                        BitmapCacheOption.None);
                bf.Freeze();
                var wb = new WriteableBitmap(bf);
                wb.Freeze();
                this.Dispatcher.Invoke(
                    new Action(() =>
                    {
                        var r = new Int32Rect(1, 1, 5, 5);
                        CroppedBitmap cb = new CroppedBitmap(wb, r);
                        cb.Freeze();
                        //set Image's source to cb....
                        Image.Source = cb;
                    }),
                    System.Windows.Threading.DispatcherPriority.ApplicationIdle);
            }
        );

code3: It works without WritableBitmap

        ThreadPool.QueueUserWorkItem((o) =>
            {
                var bf = BitmapFrame.Create(
                        new Uri("D:\\1172735642.jpg"),
                        BitmapCreateOptions.None,
                        //BitmapCreateOptions.DelayCreation,
                        BitmapCacheOption.None);
                bf.Freeze();
                var bf2 = BitmapFrame.Create(bf);
                bf2.Freeze();

                this.Dispatcher.Invoke(
                    new Action(() =>
                    {
                        var r = new Int32Rect(1, 1, 5, 5);
                        BitmapSource cb = new CroppedBitmap(bf2, r);
                        cb.Freeze();
                        //set Image's source to cb....
                        Image.Source = cb;
                    }),
                    System.Windows.Threading.DispatcherPriority.ApplicationIdle);
            }
        );
+1  A: 

When working with WPF be aware that if you create a UI object in one thread you can't access it from another thread. Your UI objects should (typically) be created the UI thread, and then you need the UI thread to access them later. No other thread will be able to access objects created on the UI thread.

If you need to access a UI object from another thread you need the UI thread Dispatcher, and then you can use this to invoke calls on the UI thread.

I've spent many hours in frustration of similar problems to this - believe me.. Check out this question - it gave me a lot of useful information on the subject.

stiank81
But, after I change the code to CroppedBitmap cb = new CroppedBitmap(new WriteableBitmap(bf), new Int32Rect(1, 1, 5, 5));no exception.why? they are still in different thread.
zunyite
Sorry - I'm not sure about that. Haven't used WriteableBitmap. Maybe the documentation can give you a hint? http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx
stiank81
+1  A: 

Following code might help you solve the issue of updating a gui element from another thread :

Module level

delegate void updateCallback(string tekst);

This is the method to update your element :

private void UpdateElement(string tekst)
{
    if (element.Dispatcher.CheckAccess() == false)
    {
        updateCallback uCallBack = new updateCallback(UpdateElement);
        this.Dispatcher.Invoke(uCallBack, tekst);
    }
    else
    { 
//update your element here
    }
 }
djerry
+1  A: 

You can look through this classes in reflector. Exception will rise in cb.Freeze(). In

CroppedBitmap cb = new CroppedBitmap(bf, new Int32Rect(1,1,5,5));

case constructor did something like this:

this.this.Source = source;

So source wasn't created in current thread, and so exception will rise. In

new WriteableBitmap(bf)

case, constructor synchronize with bf object and new source is created in current thread, so, no exceptions will rise. If you are interested in In Depth details, you can always reflect base libraries with Reflector :)

Andrew
I'v updated mycode, please see my new code 2, CroppedBitmap cb = new CroppedBitmap(wb, r);wb now in different thread, but NO exception. why ?
zunyite
No exceptions was thrown because WriteableBitmap was created to work with multiple threads (it even has lock and unlock methods) http://msdn.microsoft.com/en-en/library/system.windows.media.imaging.writeablebitmap(VS.90).aspxAnd CroppedBitmap - isn't, actually at first, i thought that only BitmapFrame doens't like call from multiple threads, i was wrong, CroppedBitmap doesn't like it to :)
Andrew
I'v updated mycode again, please see my new code 3, now the I replace WritableBitmap with BitmapFrame, It still works.
zunyite
BitmapFrame at first seems the same but actually different constructors created different classes (BitmapFrame is abstract actually), bf is BitmapFrameDecode, and bf2 is BitmapFrameEncode, they are internal classes derived from BitmapFrame, so one is support access from other thread when the other isn't. Actually some properties have complex getter that throw exception, so if you are interested in it, go go go Disassemble :)
Andrew
Thanks, I'd try to trace the code with reflector, but I have no idea how to tell if a bitmap class support thread or not.
zunyite
For this i think debugger is much better then reflector :) Reflector helps you to understand what happend and why, but with debugger you can just look through properties and find out is they are good or throw exception, but you have to create code example though :)
Andrew