I'm writing an WPF application where I need to show a Webcam feed. I was able to do this easly with the AForge framework.But when I've changed from a computer to a another computer the same code doesn't work the same way.
In the first one the webcam feed works perfectly, but in the other one this does't occur, the feed has a lot of delay, and the application doesn't work properly.
Here is the code:
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (SendOrPostCallback)delegate
{
IntPtr hBitmap = img.GetHbitmap();
System.Windows.Media.Imaging.BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
img.Dispose();
GC.Collect();
image1.Source = bitmapSource;
}, null);
}
What this code is really simple, it gets a new_frame from the webcam in a form of a Bitmap, and what I need to do is to convert it to a BitmapSource, so I can show in the image frame of the WPF. I think this conversion is the responsible of the mess that is happening, but I don't understand why it works in a computer and in the other doesn't.
The computer specs are almost the same, the processor is the same, as well the system memory.
My problem here is about performance, this code in one computer runs smoothly, and the webcam feed is presented as it should, when I port it to another PC this doesn't happen.
Here is working code based on this article.
(1) Download and install last AForge framework. (I have used version 2.2.4)
(2) Create WPF Application project.
(3) Add references to those AForge DLLs. (You can find them under C:\Program Files (x86)\AForge.NET\Framework\Release folder i.e.)
(4) Build your project. (I have used VS 2012)
(5) Add WPF Image control and name it "frameHolder".
So you have something like
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Image HorizontalAlignment="Stretch" Name="frameHolder" VerticalAlignment="Stretch" Stretch="Fill"/>
</Grid>
</Window>
(6) Add C# code:
using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
/////
namespace WpfApplication1
{
public partial class MainWindow : Window
{
VideoCaptureDevice LocalWebCam;
public FilterInfoCollection LoaclWebCamsCollection;
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
System.Drawing.Image img = (Bitmap)eventArgs.Frame.Clone();
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
frameHolder.Source = bi;
}));
}
catch (Exception ex)
{
}
}
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
LoaclWebCamsCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
LocalWebCam = new VideoCaptureDevice(LoaclWebCamsCollection[0].MonikerString);
LocalWebCam.NewFrame += new NewFrameEventHandler(Cam_NewFrame);
LocalWebCam.Start();
}
}
}
(7) Re-Build project and it works!
Note: We use first detected WebCam by default.
Make sure you have WebCam driver insalled and WebCam is working in general... :)
I know the original post is over 3 years old, but I just have been trying to figure out how to use this code. I found that the answer given by Dimi is nearly a fully functional code. However, I found that I have issues with memory leaking and the frame not being render reliably on some computers. The code worked perfectly on my beefier development computer (i7, 16GB RAM, Quadro Pro Grapthics card), but when I deployed the app on a computer with more limited resources (i5, 4GB RAM, Integrated Intel graphics), the frame disappears once in a while and the program would also crash after the system memory runs out. After searching the internet for a while, I think I finally patched together a working code based on all the feedback people had. I know that the other computer is capable of running frame capture from the webcam because I have a WinForm C# app that I wrote using AForge.NET and it has no issues rendering the frame reliably and with no memory leak. Unfortunately WPF doesn't handle graphics the same way as WinForm and we have to do this hack to get AForge.NET to work with it.
Basically, the code is the same as Dimi's except for the Cam_NewFrame method.
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
BitmapImage bi;
using(var bitmap = (Bitmap)eventArgs.Frame.Clone())
{
bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
}
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate { frameHolder.Source = bi; }));
}
catch (Exception ex)
{
//catch your error here
}
}
The changes that were made are the following:
Enclosing the bitmap handling with Using scope so that any unused memory is cleaned up right away after the end of scope.
Moving the bi.BeginInit() before dealing with the memory stream so that the bitmap is ready for the memomory dump right away.
Changing the CacheOption to OnLoad so that all the image memory gets dump right at the loading. Otherwise, it uses BitmapCacheOption.Default which could allow the image to hold on to the memory even when bi.Freeze() is issued. This caused the frame to not be rendered even with the Dispatcher.BeginInvoke is called to render the image.
So far it's been working well but if anyone else spot other issues please make a comment so we know how to fix it.
In my WPF MediaKit, I have a control called VideoCaptureElement that will render a webcam to WPF. You can also get access to the samples by hooking into the new image event and setting the EnableSampleGrabbing on the element.
Maybe the webcam on the other computer is broken/faulty? Or has one of the webcams that doesnt support the DirectShow api, which i think AForge builds on.
Related
We are developing a WPF application which will open a number of reports at the same time (just like a typical MDI application such as Excel or Visual Studio). Although it is possible to have the data context for those reports run in multiple worker threads, we still find that if the number of opened reports is really big, even the rendering of those reports (basically UserControl hosted either in a MDI environment or just in a grid area in the main view) will still make the application less responsive.
So, my idea is to at least have several areas in the main UI, each of whom will have its user control running in different UI threads. Again, imagine a typical view in visual studio, except for the menus, it has the main area of text editor, a side area which hosts for example solution explorer, and a bottom area which hosts for example error list and output. So I want these three areas to be running in three UI threads (but naturally they are hosted in one MainView, that's the part I am not sure about).
I am asking because I know it is possible to have several (top-level) windows running in different UI threads. But somebody said it doesn't apply to the user controls. Is it true? If so, what is the typical solution to my scenario, i.e., the number of opened UserControl is really big, and many of these UserControl are real-time so rendering of them takes huge amount of resources? Thanks!
Background Information on UI Threading Models
Normally an application has one "main" UI thread...and it may have 0 or more background/worker/non-UI threads where you (or the .NET runtime/framework) does background work.
(...there's a another special thread in WPF called the rendering thread but I will skip that for now...)
For example, a simple WPF Application might have this list of threads:
And a simple WinForms Application might have this list of threads:
When you create an element it is tied (has affinity) to a particular Dispatcher & thread and can only be accessed safely from the thread associated with the Dispatcher.
If you try and access properties or methods of an object from a different thread, you will usually get an exception e.g. in WPF:
In WindowsForms:
Detecting whether on UI thread in WPF and Winforms
http://www.perceler.com/articles1.php?art=crossthreads1
Any modifications to the UI need to be performed on the same thread on which a UI element was created...so background threads use Invoke/BeginInvoke to get that work run on the UI thread.
Demo to Demonstrate Issues with Element Creation on non-UI Thread
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<StackPanel x:Name="mystackpanel">
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;
namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Thread m_thread1;
Thread m_thread2;
Thread m_thread3;
Thread m_thread4;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CreateAndAddElementInDifferentWays();
}
void CreateAndAddElementInDifferentWays()
{
string text = "created in ui thread, added in ui thread [Main STA]";
System.Diagnostics.Debug.WriteLine(text);
CreateAndAddTextChild(text);
// Do NOT use any Joins with any of these threads, otherwise you will get a
// deadlock on any "Invoke" call you do.
// To better observe and focus on the behaviour when creating and
// adding an element from differently configured threads, I suggest
// you pick "one" of these and do a recompile/run.
ParameterizedThreadStart paramthreadstart1 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnThread);
m_thread1 = new Thread(paramthreadstart1);
m_thread1.SetApartmentState(ApartmentState.STA);
m_thread1.Start("[STA]");
//ParameterizedThreadStart paramthreadstart2 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnUIThread);
//m_thread2 = new Thread(paramthreadstart2);
//m_thread2.SetApartmentState(ApartmentState.STA);
//m_thread2.Start("[STA]");
//ParameterizedThreadStart paramthreadstart3 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnThread);
//m_thread3 = new Thread(paramthreadstart3);
//m_thread3.SetApartmentState(ApartmentState.MTA);
//m_thread3.Start("[MTA]");
//ParameterizedThreadStart paramthreadstart4 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnUIThread);
//m_thread4 = new Thread(paramthreadstart4);
//m_thread4.SetApartmentState(ApartmentState.MTA);
//m_thread4.Start("[MTA]");
}
//----------------------------------------------------------------------
void WorkCreatedOnThreadAddedOnThread(object parameter)
{
string threadingmodel = parameter as string;
string text = "created in worker thread, added in background thread, " + threadingmodel;
System.Diagnostics.Debug.WriteLine(text);
CreateAndAddTextChild(text);
}
void WorkCreatedOnThreadAddedOnUIThread(object parameter)
{
string threadingmodel = parameter as string;
string text = "created in worker thread, added in ui thread via invoke" + threadingmodel;
System.Diagnostics.Debug.WriteLine(text);
TextBlock tb = CreateTextBlock(text);
if (tb != null)
{
// You can alternatively use .Invoke if you like!
DispatcherOperation dispop = Dispatcher.BeginInvoke(new Action(() =>
{
// Get this work done on the main UI thread.
AddTextBlock(tb);
}));
if (dispop.Status != DispatcherOperationStatus.Completed)
{
dispop.Wait();
}
}
}
//----------------------------------------------------------------------
public TextBlock CreateTextBlock(string text)
{
System.Diagnostics.Debug.WriteLine("[CreateTextBlock]");
try
{
TextBlock tb = new TextBlock();
tb.Text = text;
return tb;
}
catch (InvalidOperationException ex)
{
// will always exception, using this to highlight issue.
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return null;
}
public void AddTextBlock(TextBlock tb)
{
System.Diagnostics.Debug.WriteLine("[AddTextBlock]");
try
{
mystackpanel.Children.Add(tb);
}
catch (InvalidOperationException ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public void CreateAndAddTextChild(string text)
{
TextBlock tb = CreateTextBlock(text);
if (tb != null)
AddTextBlock(tb);
}
}
}
Secondary UI thread aka "Creating a top-level Window on another thread"
It's possible to create secondary UI-threads, so long as you mark the thread as using the STA apartment model, and create a Dispatcher (e.g. use Dispatcher.Current) and start a "run" loop (Dispatcher.Run()) so the Dispatcher can service messages for the UI elements created on that thread.
Dispatcher to Thread relationships in WPF
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
http://www.diranieh.com/NET_WPF/Threading.htm
BUT an element created in one UI thread can't be put into the
logical/visual tree of another element which is created on a different
UI thread.
Workaround Technique for mixing elements created on different UI threads
There is a limited workaround technique, which may provide you with some ability to compose the rendering of an element created in one UI thread with the visual tree created in a different thread...by using HostVisual. See this example:
https://dwayneneed.github.io/wpf/2007/04/26/multithreaded-ui-hostvisual.html
No, UserControls are tied to the UI thread. Even if you were able to initialize them elsewhere, you'd have issues adding them to the main UI as they'd belong to a different thread.
You CAN split rendering of the visual tree across different threads.
See this article for a good explanation and an example that renders video output.
http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx
But doing this is only really justifiable when the actual rendering of the Visual is implemented elsewhere or it's technologically very odd in a WPF application such as rendering a Direct3D scene as a Visual.
The important note here, as is mentioned in the article, is that if the secondary threads render WPF XAML then you loose input events because routed events can't cross the thread boundary.
I'm working with Emgu Cv in Winforms to do face recognition using Kinect. Now, i want to move to WPF. However, the EmguCv library support only Bitmap class.
Can i use the Bitmap class (used in Winforms) in WPF ? if not, is there an other method to use Emgu cv with kinect in WPF?
Thanks.
System.Drawing.Bitmap can not be used directly as image source for WPF, so you have to convert it to System.Windows.Media.Imaging.BitmapSource.
The best way to do it is by using Imaging.CreateBitmapSourceFromHBitmap.
You can use an extension method:
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
IntPtr ip = source.GetHbitmap();
try
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ip,
IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(ip);
}
}
Please note that you must invoke DeleteObject, because Bitmap.GetHbitmap() leaks a GDI handle (see this answer).
Once you have a BitmapSource, you can display it using an Image control and by setting the Source property.
You can read more about WPF imaging in this article: Imaging Overview
I'm using a thread to get an image from a website and shoot it back to the parent form (WPF) to display. I ran into an issue and have managed to debug it to this example:
public void Watch()
{
while (true)
{
Bitmap bmp = new Bitmap(1, 1);
BitmapImage bmpImg = new BitmapImage();
this.SetImage(bmp, bmpImg);
}
}
public delegate void SetImageCallback(Bitmap bmp, BitmapImage bmpImg);
private void SetImage(Bitmap bmp, BitmapImage bmpImg)
{
if (!this.imgVideo.Dispatcher.CheckAccess())
{
SetImageCallback del = new SetImageCallback(SetImage);
this.Dispatcher.Invoke(del, bmp, bmpImg);
}
else
{
Bitmap bitmap = bmp;
BitmapImage bitmapImage = bmpImg;
}
}
Keep in mind that Watch() runs on its own thread. If I use the bitmap object (which I can use with PictureBox in Window Forms) everything works great. That is, debugging this code, when I get to the line
Bitmap bitmap = bmp;
And inspect the variable bmp, everything is great and works as expected. HOWEVER, when I get to the next line
BitmapImage bitmapImage = bmpImg;
And inpsect the variable bmpImage, I get a ton of System.InvalidOperationException's. When this is in practice and gets assigned to a WPF Image object, it says that "The calling thread cannot access this object because a different thread owns it." Why am I running into this issue with WPF BitmapImages (which are required to set an ImageSource) but NOT in Windows Forms Bitmap objects (which can be used to set a PictureBox)? How do I fix this in WPF?
Most objects in WPF are of this category: they cannot be shared between different threads. However certain low-level resources such as brushes and bitmaps are derived from a special class called Freezable that if frozen can be shared between different threads. Of course once an object is frozen is can no longer be modified in any way. To freeze a freezable object simply call Freeze and this will prevent cross-thread exceptions.
Instead of
if (!this.imgVideo.Dispatcher.CheckAccess())
{
SetImageCallback del = new SetImageCallback(SetImage);
this.Dispatcher.Invoke(del, bmp, bmpImg);
}
try using :
if (!App.Current.Dispatcher.CheckAccess())
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<CustomObject>(SetImage),CustomeObjectInstance );
Here Cutom object will be a wrapper class wrapping
Bitmap bmp, BitmapImage bmpImg
Obviously, your SetImage signature will change to
SetImage(CutomObject custObj)
I have not tested the code but this may solve the issue.
Let us know if this works so that some poor soul can be benefitted from this post.
All the best!
Sid
I think this is a bug in the WPF framework, without going into depths of my program and why I am doing what I am doing, I wrote a simple test application to prove my theory.
Can this issue be confirmed? What are possible workarounds for a series of dialogs to be executed before putting the application into its run loop?
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace ShowDialogWindow
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
Window testWindow = new Window();
testWindow.ShowDialog();
testWindow.Close();
// THE CODE BELOW WILL NOT SHOW THE NEXT WINDOW
Window testWindow2 = new Window();
testWindow2.ShowDialog();
testWindow2.Close();
}
}
}
Update:
Sure I can change my design to accommodate this behaviour. What I was trying to do was really simple however.
I have derived my MyApplication from Application. On the Main(), I initiate a series of start checks, for example, license information, splash screen, connectivity and configuration checks, ect. When I get my all clear, I call MyApplicatiom.Run(MyMainForm).
This design is based on a normal Windows application which works without a problem.
Thanks a lot for your help, and I will remember that you cannot call ShowDialog() before application.Run() as it simply innitializes a shutdown. I would have thought however that a shutdown sequence should only be initiated after a App.Run() instruction.
Please correct me if I am understanding this wrong.
Not a bug. The default ShutdownMode of Application is OnLastWindowClosed, so as soon as the first window is closed your application will start shutting down! Change to OnExplicitShutdown and it will work, but you'll have to manage the shutdown.
I think you probably just need to rethink what you're actually trying to achieve. Why would you display two subsequent dialog windows during the startup of your app?
For anyone that has the same problem, here is how you can get around it:
public App()
{
// Preserve and temporarily switch shutdown mode
var oldShutdownMode = ShutdownMode;
ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new Window();
var result = dialog.ShowDialog();
dialog = new Window();
result = dialog.ShowDialog(); // This will show!
// Reset shutdown mode to original value
ShutdownMode = oldShutdownMode;
}
I have a WPF application that uses a component that sends a bitmap to my application as they become available, I receive those bitmaps in a delegate I pass to this component.
I created a new thread for this process and it works very well, the bitmaps comes as MemoryStream and I just create the BitmapSource object from this stream inside a Dispatcher.BeginInvoke method call. After I have the BitmapSource object, I add them to a StackPanel so the user can see a queue of images available to work. So far so good...
The problem is that those bitmaps are quite big, like 3000x2000+ pixels, and it takes about 50~ms to create these bitmaps and add to the queue, and when this code is executed, the onde inside the BeginInvoke call, it blocks the UI for this time, causing a very annoying behavior, (to reproduce this, just call Thread.Sleep(50) every 5 seconds).
How can I fix this so the user is always responsive?
thanks!
There are two ideas you might want to consider:
Don't create the BitmapSource inside Dispatcher.Invoke. That would practically create it inside the UI thread, slowing things down. Instead, create it in the background thread, freeze it, and then pass the frozen BitmapSource to the foreground thread.
Depending on your application, perhaps the StackPanel doesn't need the full 3000x2000 resolution? If that is the case, consider resizing down the images in the background thread, just before you freeze them.
The following code does #1 above:
Window1.xaml
<Window x:Class="BitmapFrameDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Image Name="image"/>
</Grid>
</Window>
Window1.xaml.cs
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace BitmapFrameDemo {
public partial class Window1 : Window {
private Thread thread = null;
private Dispatcher dispatcher = null;
private void ThreadMain() {
// obtain the image memory stream
WebRequest request = WebRequest.Create("http://stackoverflow.com/content/img/so/logo.png");
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
// create a bitmap source while still in the background thread
PngBitmapDecoder decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapFrame frame = decoder.Frames[0];
// freeze the bitmap source, so that we can pass it to the foreground thread
BitmapFrame frozen = (BitmapFrame) frame.GetAsFrozen();
dispatcher.Invoke(new Action(() => { image.Source = frozen; }), new object[] { });
}
public Window1() {
InitializeComponent();
dispatcher = Dispatcher.CurrentDispatcher;
thread = new Thread(new ThreadStart(this.ThreadMain));
thread.Start();
}
}
}
alt text http://www.freeimagehosting.net/uploads/66bdbce78a.png
Really all you need to do is set the IsAsync to True on your binding to the image. I would however recomend using a PriorityBinding and preparing some form of default image that the user can see so they know it isn't fully loaded.
<StackPanel>
<Image>
<Image.Source>
<PriorityBinding>
<Binding Path="SlowImage"
IsAsync="True" />
<Binding Path="DefaultImage" />
</PriorityBinding>
</Image.Source>
</Image>
</StackPanel>
public partial class Window1 : Window, INotifyPropertyChanged
{
public Window1()
{
InitializeComponent();
DefaultImage = new BitmapImage(new Uri("http://stackoverflow.com/content/img/so/logo.png"));
SlowImage = new BitmapImage(new Uri("http://serverfault.com/content/img/sf/logo.png"));
this.DataContext = this;
}
private BitmapImage myDefaultImage;
public BitmapImage DefaultImage
{
get { return this.myDefaultImage; }
set
{
this.myDefaultImage = value;
this.NotifyPropertyChanged("Image");
}
}
private BitmapImage mySlowImage;
public BitmapImage SlowImage
{
get
{
Thread.Sleep(5000);
return this.mySlowImage;
}
set
{
this.mySlowImage = value;
this.NotifyPropertyChanged("SlowImage");
}
}
#region INotifyPropertyChanged Members
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Two possibilities come to mind quickly.
The first is to use a BackgroundWorker to perform the transformation of the MemoryStream to a Bitmap.
the Second is to pass off that transformation to a ThreadPool.
While not an entirely satifsfactory answer for you, I would suggest you listen to this Hanselminutes podcast with Ian Griffiths as it covers a very similar program and the proper way to architect it to get the performance you are looking for.
Some specifics may be found on Ian's blog: http://www.interact-sw.co.uk/iangblog/