I have created a thread, but the thread pauses the main process after I start it. The thread loads some images from google, but when the internet connection is lost the user interface is unusable.
This is the thread:
string searchWord = "car photo";
PhotoSearchThread = new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
if (!string.IsNullOrWhiteSpace(searchWord))
{
string html = GetHtmlCode(searchWord);
SearchedImagesUrls = GetUrls(html);
this.Dispatcher.Invoke(() =>
{
if (SearchedImagesUrls.Count > 0)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(SearchedImagesUrls[0]);
image.EndInit();
SelectPhotoImage.Source = image;
}
});
}
});
PhotoSearchThread.Start();
Well threads should run simultaneously, then why this thread is interrupting other threads?
Invoke is used to run code on the main or UI thread. Specifically for updating UI elements as those can only be updated by that thread. Currently you have code that loads the image in the Invoke. Instead you should only put the part of the code that updates the UI inside of the Invoke.
PhotoSearchThread = new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
if (!string.IsNullOrWhiteSpace(searchWord))
{
string html = GetHtmlCode(searchWord);
SearchedImagesUrls = GetUrls(html);
if (SearchedImagesUrls.Count > 0)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(SearchedImagesUrls[0]);
image.EndInit();
this.Dispatcher.Invoke(() =>
{
SelectPhotoImage.Source = image;
});
}
}
});
I found the solution:
1.Add following using: using System.ComponentModel;
2.Declare background worker:
private readonly BackgroundWorker worker = new BackgroundWorker();
3.Subscribe to events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
4.Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
5.Run worker async whenever your need.
worker.RunWorkerAsync();
Also if you want to report process progress you should subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork method to raise an event. Also set following: worker.WorkerReportsProgress = true; (thanks to #zagy)
source: How to use WPF Background Worker
Hope this help.
Related
I am Capturing image from a finger print Scanner and i want to display the captured image live in an Image control.
//Onclick of a Button
Thread WorkerThread = new Thread(new ThreadStart(CaptureThread));
WorkerThread.Start();
So i created a thread as above and called the method that captures the image from the device and sets the source of the Image control as follows.
private void CaptureThread()
{
m_bScanning = true;
while (!m_bCancelOperation)
{
GetFrame();
if (m_Frame != null)
{
MyBitmapFile myFile = new MyBitmapFile(m_hDevice.ImageSize.Width, m_hDevice.ImageSize.Height, m_Frame);
MemoryStream BmpStream = new MemoryStream(myFile.BitmatFileData);
var imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.StreamSource = BmpStream;
imageSource.EndInit();
if (imgLivePic.Dispatcher.CheckAccess())
{
imgLivePic.Source = imageSource;
}
else
{
Action act = () => { imgLivePic.Source = imageSource; };
imgLivePic.Dispatcher.BeginInvoke(act);
}
}
Thread.Sleep(10);
}
m_bScanning = false;
}
Now when i Run the project it throws an Exception on line Action act = () => { imgLivePic.Source = imageSource; }; Saying "The Calling thread Can Not Access this object because a different thread owns it".
i did some research and i found out that if i want to use UI controls over a NON-UI thread i should use Dispatcher.Invoke method, which as you can see i have but i am still getting the same exception.
can someone please tell me what am i doing wrong?
The BitmapImage does not necessarily need to be created in the UI thread. If you Freeze it, it will later be accessible from the UI thread. Thus you will also reduce the resource consumption of your application. In general, you should try to freeze all Freezables if possible, especially bitmaps.
using (var bmpStream = new MemoryStream(myFile.BitmatFileData))
{
imageSource.BeginInit();
imageSource.StreamSource = bmpStream;
imageSource.CacheOption = BitmapCacheOption.OnLoad;
imageSource.EndInit();
}
imageSource.Freeze(); // here
if (imgLivePic.Dispatcher.CheckAccess())
{
imgLivePic.Source = imageSource;
}
else
{
Action act = () => { imgLivePic.Source = imageSource; };
imgLivePic.Dispatcher.BeginInvoke(act);
}
The BitmapImage itself needs to be constructed on the Dispatcher thread.
I have a method which is a time consuming one and therefore I have been trying to implement a BackgroundWorker, but it does not allow accessing UI controls which I have read and tried (hacking it) but to no avail.
What my method does: Creates a new BitmapImage, sets the source local or streamed (the parameter), writes it to a new WriteableBitmap, which is used for ConvertToGrayscale and then saves the BW Copy to IsolatedStorage in a folder.
So all this happens quite fast. But, only when I have say less than 25 Source Images. If I have about 100+ Images, this takes considerably long like 20 seconds or more and therefore, I would like to show a ProgressBar in the same PhoneApplicationPage but I have been struggling with how to not block the UI and show the ProgressBar while the method is doing its work.
This is the code that I have:
void GetImages()
{
if (!myIsolatedStorage.DirectoryExists("ImagesBW") && !_appsettings.Contains("_update"))
{
myIsolatedStorage.CreateDirectory("ImagesBW ");
for (int i = 0; i < coll.Desserts.Count; i++)
{
BitmapImage bmp = new BitmapImage();
bmp.CreateOptions = BitmapCreateOptions.None;
if (coll.Desserts[i].HasAssociatedImage)
{
bmp.SetSource(coll.Desserts[i].GetImage());
WriteableBitmap wb = new WriteableBitmap(bmp);
ConvertToGrayscale(wb);
BitmapImage bit = ConvertWBtoBI(wb);
SaveBWCopy(bi, i.ToString());
}
else
{
bmp.UriSource = new Uri("/Assets/Images/MissingArt.png", UriKind.Relative);
WriteableBitmap wb = new WriteableBitmap(bmp);
ConvertToGrayscale(wb);
BitmapImage bit = ConvertWBtoBI(wb);
SaveBWCopy(bi, i.ToString());
}
}
_appsettings["_firstLaunch"] = "false";
_appsettings.Save();
}
else if (myIsolatedStorage.DirectoryExists("ImagesBW ") && _appsettings.Contains("_update"))
{
string[] files = myIsolatedStorage.GetFileNames("ImagesBW/*");
for (int s = 0; s < files.Length; s++)
{
myIsolatedStorage.DeleteFile("ImagesBW/" + s + ".jpg");
}
myIsolatedStorage.DeleteDirectory("ImagesBW");
myIsolatedStorage.CreateDirectory("ImagesBW");
for (int i = 0; i < coll.Desserts.Count; i++)
{
BitmapImage bmp = new BitmapImage();
bmp.CreateOptions = BitmapCreateOptions.None;
if (coll.Desserts[i].HasAssociatedImage)
{
bmp.SetSource(coll.Desserts[i].GetImage());
WriteableBitmap wb = new WriteableBitmap(bmp);
ConvertToGrayscale(wb);
BitmapImage bit = ConvertWBtoBI(wb);
SaveBWCopy(bi, i.ToString());
}
else
{
bmp.UriSource = new Uri("/Assets/Images/MissingArt.png", UriKind.Relative);
WriteableBitmap wb = new WriteableBitmap(bmp);
ConvertToGrayscale(wb);
BitmapImage bit = ConvertWBtoBI(wb);
SaveBWCopy(bi, i.ToString());
}
}
_appsettings.Remove("_update");
_appsettings.Save();
}
btnStart.IsEnabled = true;
}
Backgroundorker is your best bet. There are huge amount of resources on the web how to implement it. But the idea behind is that Backgroundworker runs in a separate thread from the UI. You can access main thread UI by two methods, a delegate or through the Backgroundworker's ProgressChanged method, which gives access to you UI thread.
Lets say you have this.
public MainWindow()
{
InitializeComponent();
//if you want to disable you button you can do it here
BackgroundWorker _bw = new BackgroundWorker();
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
_bw.WorkerReportsProgress = true;
_bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
_bw.RunWorkerAsync();
//or here
//Display progress bar here too
}
void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//this can give you access to the UI after work is completed
// to check that everything is ok or hide progress bar
}
void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//this will give you access to the UI in the middle of you work like update progress bar
}
void _bw_DoWork(object sender, DoWorkEventArgs e)
{
//actual work here including GetImages
//work work work
GetImages();
}
In you getimages method after the SaveBWCopy you can add this to update the progress bar
_bw.ReportProgress(int progress )
//progress is the percentage you want to send to progress bar to display that is going to be in the e eventargument you passed.
I have button on my window. After user clickes the button i want my application to animate loading label (with rotationg it), during the other thread gets some data from database. After loading data from DB animation must end. The task seems simple , but it doesn't work.
The problem is that animation whatever i do animation starts only after loading from the database when it is not needed.
Help please. Here some code:
private void LoginButtonClick(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ThreadStart(
delegate()
{
DispatcherOperation dispatcherOp =
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
delegate()
{
var da = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(1)));
var rt = new RotateTransform();
loadingLabel.RenderTransform = rt;
loadingLabel.RenderTransformOrigin = new Point(0.5, 0.5);
da.RepeatBehavior = RepeatBehavior.Forever;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
}));
dispatcherOp.Completed += new EventHandler(DispatcherOpCompleted);
}));
thread.Start();
}
void DispatcherOpCompleted(object sender, EventArgs e)
{
//Loading From Database
}
The Dispatcher.Completed event is executed on the main UI thread. Your worker thread is just queueing the dispatcher operation and exiting. Instead of creating a thread that starts the animation and then doing your database loading in the Completed handler, just start your animation in the main thread, and then create a worker thread to do the database loading.
private void LoginButtonClick(object sender, RoutedEventArgs e)
{
var da = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(1)));
var rt = new RotateTransform();
loadingLabel.RenderTransform = rt;
loadingLabel.RenderTransformOrigin = new Point(0.5, 0.5);
da.RepeatBehavior = RepeatBehavior.Forever;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
Thread thread = new Thread(new ThreadStart(LoadData));
thread.Start();
}
void LoadData()
{
//Loading From Database
// Use a Dispatch.BeginInvoke here to stop the animation
// and do any other UI updates that use the results of the database load
}
I have written code to save an image which is generated by the application. The size of the image is around 32-35 MB. While saving the image to a BMB file, it is taking a long time, around 3-5 secs. For this purpose, I have used a background worker but when running the background worker, it shows an error like..."can't access the object as it is created on different thread".
Following is the code:
private void btnSaveDesign_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
sfd.Title = "Save design as...";
sfd.Filter = "BMP|*.bmp";
if (sfd.ShowDialog() == true)
{
ww = new winWait();
ww.Show();
System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker();
bw.DoWork += new System.ComponentModel.DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
fName = sfd.FileName;
cache = new CachedBitmap((BitmapSource)imgOut.Source, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
bw.RunWorkerAsync();
}
}
void bw_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
ww.Close();
}
void bw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(cache)); //here... it says cant access...
using (FileStream file = File.OpenWrite(fName))
{
encoder.Save(file);
}
}
I have declared "cache" as a global object. (A similar trick worked when I was programming in Windows Forms with VB.NET.)
ww is the wait window that I want to be displayed while the precess is being executed.
How to do this? Is there any other simple method for multi threading in WPF?
When WPF objects are created they are assigned to a Dispatcher object. This disallows any threads other than the creating thread to access the object. This can be circumvented by freezing the object by calling the freeze method. You would need to call Freeze on your bitmapsource object. Once you have frozen your object it becomes uneditable
Your problem comes about because you are accessing an object which is not created by the background worker thread. Normally this would happen if you access a UI control which is created in the main thread and accessed from different thread.
Use the code below.
Dispatcher.Invoke
(
new Action(
delegate()
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(cache));
using (FileStream file = File.OpenWrite(fName))
{
encoder.Save(file);
}
}
)
);
I think you have to pass cache as a parameter to the new thread:
bw.RunWorkerAsync(cache);
and get it from the DoWork method:
var cache=(CacheType) e.Argument;
.NET framework provides a simple way to get started in threading with
the BackgroundWorker component. This wraps much of the complexity and
makes spawning a background thread relatively safe. In addition, it
allows you to communicate between your background thread and your UI
thread without doing any special coding. You can use this component
with WinForms and WPF applications. The BackgroundWorker offers
several features which include spawning a background thread, the
ability to cancel the background process before it has completed, and
the chance to report the progress back to your UI.
public BackgroudWorker()
{
InitializeComponent();
backgroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker"));
}
private int DoSlowProcess(int iterations, BackgroundWorker worker, DoWorkEventArgs e)
{
int result = 0;
for (int i = 0; i <= iterations; i++)
{
if (worker != null)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return result;
}
if (worker.WorkerReportsProgress)
{
int percentComplete =
(int)((float)i / (float)iterations * 100);
worker.ReportProgress(percentComplete);
}
}
Thread.Sleep(100);
result = i;
}
return result;
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
int iterations = 0;
if (int.TryParse(inputBox.Text, out iterations))
{
backgroundWorker.RunWorkerAsync(iterations);
startButton.IsEnabled = false;
cancelButton.IsEnabled = true;
outputBox.Text = "";
}
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
// TODO: Implement Cancel process
this.backgroundWorker.CancelAsync();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// e.Result = DoSlowProcess((int)e.Argument);
var bgw = sender as BackgroundWorker;
e.Result = DoSlowProcess((int)e.Argument, bgw, e);
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
workerProgress.Value = e.ProgressPercentage;
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
outputBox.Text = "Canceled";
workerProgress.Value = 0;
}
else
{
outputBox.Text = e.Result.ToString();
workerProgress.Value = 0;
}
startButton.IsEnabled = true;
cancelButton.IsEnabled = false;
}
What's the best way to asynchronously load an BitmapImage in C# using WPF?
I was just looking into this and had to throw in my two cents, though a few years after the original post (just in case any one else comes looking for this same thing I was looking into).
I have an Image control that needs to have it's image loaded in the background using a Stream, and then displayed.
The problem that I kept running into is that the BitmapSource, it's Stream source and the Image control all had to be on the same thread.
In this case, using a Binding and setting it's IsAsynch = true will throw a cross thread exception.
A BackgroundWorker is great for WinForms, and you can use this in WPF, but I prefer to avoid using the WinForm assemblies in WPF (bloating of a project is not recommended, and it's a good rule of thumb too). This should throw an invalid cross reference exception in this case too, but I didn't test it.
Turns out that one line of code will make any of these work:
//Create the image control
Image img = new Image {HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, VerticalAlignment = System.Windows.VerticalAlignment.Stretch};
//Create a seperate thread to load the image
ThreadStart thread = delegate
{
//Load the image in a seperate thread
BitmapImage bmpImage = new BitmapImage();
MemoryStream ms = new MemoryStream();
//A custom class that reads the bytes of off the HD and shoves them into the MemoryStream. You could just replace the MemoryStream with something like this: FileStream fs = File.Open(#"C:\ImageFileName.jpg", FileMode.Open);
MediaCoder.MediaDecoder.DecodeMediaWithStream(ImageItem, true, ms);
bmpImage.BeginInit();
bmpImage.StreamSource = ms;
bmpImage.EndInit();
//**THIS LINE locks the BitmapImage so that it can be transported across threads!!
bmpImage.Freeze();
//Call the UI thread using the Dispatcher to update the Image control
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
img.Source = bmpImage;
img.Unloaded += delegate
{
ms.Close();
ms.Dispose();
};
grdImageContainer.Children.Add(img);
}));
};
//Start previously mentioned thread...
new Thread(thread).Start();
Assuming you're using data binding, setting Binding.IsAsync property to True seems to be a standard way to achieve this.
If you're loading the bitmap in the code-behind file using background thread + Dispatcher object is a common way to update UI asynchronous
This will allow you to create the BitmapImage on the UI thread by using the HttpClient to do the async downloading:
private async Task<BitmapImage> LoadImage(string url)
{
HttpClient client = new HttpClient();
try
{
BitmapImage img = new BitmapImage();
img.CacheOption = BitmapCacheOption.OnLoad;
img.BeginInit();
img.StreamSource = await client.GetStreamAsync(url);
img.EndInit();
return img;
}
catch (HttpRequestException)
{
// the download failed, log error
return null;
}
}
To elaborate onto aku's answer, here is a small example as to where to set the IsAsync:
ItemsSource="{Binding IsAsync=True,Source={StaticResource ACollection},Path=AnObjectInCollection}"
That's what you would do in XAML.
BitmapCacheOption.OnLoad
var bmp = await System.Threading.Tasks.Task.Run(() =>
{
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.UriSource = new Uri(path);
img.EndInit();
ImageBrush brush = new ImageBrush(img);
}
Use or extend System.ComponentModel.BackgroundWorker:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Personally, I find this to be the easiest way to perform asynchronous operations in client apps. (I've used this in WinForms, but not WPF. I'm assuming this will work in WPF as well.)
I usually extend Backgroundworker, but you dont' have to.
public class ResizeFolderBackgroundWorker : BackgroundWorker
{
public ResizeFolderBackgroundWorker(string sourceFolder, int resizeTo)
{
this.sourceFolder = sourceFolder;
this.destinationFolder = destinationFolder;
this.resizeTo = resizeTo;
this.WorkerReportsProgress = true;
this.DoWork += new DoWorkEventHandler(ResizeFolderBackgroundWorker_DoWork);
}
void ResizeFolderBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DirectoryInfo dirInfo = new DirectoryInfo(sourceFolder);
FileInfo[] files = dirInfo.GetFiles("*.jpg");
foreach (FileInfo fileInfo in files)
{
/* iterate over each file and resizing it */
}
}
}
This is how you would use it in your form:
//handle a button click to start lengthy operation
private void resizeImageButtonClick(object sender, EventArgs e)
{
string sourceFolder = getSourceFolderSomehow();
resizer = new ResizeFolderBackgroundWorker(sourceFolder,290);
resizer.ProgressChanged += new progressChangedEventHandler(genericProgressChanged);
resizer.RunWorkerCompleted += new RunWorkerCompletedEventHandler(genericRunWorkerCompleted);
progressBar1.Value = 0;
progressBar1.Visible = true;
resizer.RunWorkerAsync();
}
void genericRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
//signal to user that operation has completed
}
void genericProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
//I just update a progress bar
}