0

I am using WPF 3.5.

I have while loop in a backgroundworker_DoWork event, which will continuously stream images from a DSLR.

Initially, the streamed image will be displayed in a PictureBox

<Grid>
    <WindowsFormsHost VerticalAlignment="Top" Background="Transparent" Height="500">
        <wf:PictureBox x:Name="picLiveView" />
    </WindowsFormsHost>
</Grid>

Here is the code behind:

        while (!liveViewExit)
        {
            try
            {
                if (picImage != null)
                    lock (mylock)
                    {
                        this.picLiveView.Image = (Image)picImage.Clone();
                    }
            }
            catch
            {

            }
        }

This works fine.

However, when I try to change the PictureBox to WPF Image control, I have this error when I assigned the BitmapImage to the WPF image control:

{"The calling thread cannot access this object because a different thread owns it."}

        MemoryStream ms = new MemoryStream();
        bmp.Save(ms, ImageFormat.Png);
        ms.Position = 0;
        BitmapImage bi = new BitmapImage();
        bi.BeginInit();
        bi.StreamSource = ms;
        bi.EndInit();

        try
        {
            if (bi != null)
            {
                this.imageBox.Source = bi;
            }
        }
        catch
        {

        }

Why the .NET 2 PictureBox control works while .NET 3.5 WPF Image control doesn't?

I tried this code:

BackgroundWorker bg = new BackgroundWorker();
Dispatcher disp = Dispatcher.CurrentDispatcher;
bg.DoWork += (sender, e) =>
{
    // load your data
    disp.Invoke(new Action(/* a method or lambda that do the assignment */));
}
bg.RunWorkerCompleted += anotherMethodOrLambda; // optional
bg.RunWorkerAsync(/*an argument object that will be visible in e.Argument*/);

It doesn't have any error, but the image doesn't refresh. The while loop make the application not responding.

billy_flow
  • 81
  • 4
  • 9

1 Answers1

2

you need to instantiate a Dispatcher object in main thread, and use it in the background worker, calling it's Invoke method.

Here's a sample of code:

BackgroundWorker bg = new BackgroundWorker();
Dispatcher disp = Dispatcher.CurrentDispatcher;
bg.DoWork += (sender, e) =>
{
    // load your data
    disp.Invoke(new Action(/* a method or lambda that do the assignment */));
}
bg.RunWorkerCompleted += anotherMethodOrLambda; // optional
bg.RunWorkerAsync(/*an argument object that will be visible in e.Argument*/);
BertuPG
  • 653
  • 4
  • 6
  • 1
    This answers to "How make it work in WPF?" question, but doesn't answer why it works for WinForms. The answer lies in different approaches to UI thread management in both technologies. I'm not sure whether @billy_flow was really interested in the latter. – Anvaka Feb 09 '11 at 10:31
  • @Anvaka: ok, I'll wait your answer with detailed description of these differences. – BertuPG Feb 09 '11 at 10:59
  • @BertuPG: nah, I upvoted yours :). I think @billy_flow was indeed interested in how to make it work for WPF image, not in the differences between WPF/WinForms threading model. Added the comment just in case we are both wrong. – Anvaka Feb 09 '11 at 11:05
  • @Anvaka: thanks ;) Anyway, althoug I knew the "workaround" (or better: the proper way), I'm also interested in these details. I'll google this topic... bye! – BertuPG Feb 09 '11 at 11:55
  • @BertuPG: Why are you messing around with a `Dispatcher` instead of just setting `BackgroundWorker.ReportsProgress` and handling `ProgressChanged`? – Robert Rossney Feb 09 '11 at 19:13
  • I am interested to know how both technologies work for UI thread management. Anyone link or explanation? – billy_flow Feb 10 '11 at 01:37
  • The code doesn't really work. It becomes infinite loop in the same thread? – billy_flow Feb 10 '11 at 02:03
  • @billy_flow: this piece of code comes from one of my real projects, and I'm sure it works. Of course it's not complete, but just a "template", since I removed uninteresting lines. If you think it go in infinite loop, just put a breakpoint in assignment statement. – BertuPG Feb 10 '11 at 09:18
  • @Robert Rossney: long time ago I tryed this way, but often also the ProgressChanged event is not executed on main thread, and you still need the Dispatcher. Same in WinForms was needed to concern about threads while assigning many controls' properties (not all) to not occur in cross-threading exeptions, even in ProgressChanged event handler. – BertuPG Feb 10 '11 at 09:28
  • The `ProgressChanged` thread executes on the thread that created the `BackgroundWorker`. As long as you create the `BackgroundWorker` on the UI thread, you'll never get a cross-threading exception. That's what the event is *for*. – Robert Rossney Feb 10 '11 at 18:12
  • @Robert Rossney: ok, post your complete answer, with examples, and sure the OP will mark it as the correct solution, and you will get many upvotes. Bye. – BertuPG Feb 11 '11 at 09:24