Background thread in Window_Loaded event threading error - c#

I have to load a window and in Window_Loaded I have to load some variables and show it on Window.
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
try
{
//code to download some variables which will show on UI of Window Loading
}
catch (Exception ex)
{
//The calling thread cannot access this object because a different thread owns it.
}
};
worker.RunWorkerCompleted += (o, ea) =>
{
};
worker.RunWorkerAsync();
}
But I am getting a threading exception. Is there any way to show the variables value on window from DoWork of Backgroundworker?

You should retrieve the data you need in the DoWork section, then assign it to ea.Result, which will make it available in the RunWorkerCompleted section.
In the RunWorkerCompleted section, you can access ea.Result again, casting the object back to whatever type you assigned in DoWork, and apply the data as needed to your UI controls.
worker.DoWork += (o, ea) =>
{
ea.Result = GetMyData();
};
worker.RunWorkerCompleted += (o, ea) =>
{
var myData = (myDataType)ea.Result;
// Assign myData as needed to UI components...
};

You need to let Dispatcher schedule your code to execute on UI thread and marshal necessary parameters. Try something like this:
Dispatcher.Invoke(
new Action<string>(() =>
{
// Access UI from here
}),
DispatcherPriority.Normal
);
Although this (or something like this, since this is notepad code) will solve your problem, you should consider using MVVM pattern in your implementation. Then you will be able to make changes to ViewModel (just update data) and UI will update accordingly.

Related

how to immediately update displayed textbox before a long loop

My event handler UIButton440_TouchUpInside( UIButton sender ) updates my TextBox.Text then does a long loop, then returns. But Textbox doesn't update until after return.
How can I display the updated TextBox immediately?
(Like vb.net does with application.doEvents)
You can try firing a thread to do your long loop. I usually like using workers for that, something like this:
txt.Text = "abc";
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
//Long loop
};
worker.RunWorkerCompleted += (o, ea) =>
{
//Do something when the loop ends
};
worker.RunWorkerAsync();
Assuming you're using Windows Forms, you can call the Update method on the TextBox to force a refresh.
yourTextBox.Update();

BackgroundWorker in ASP.NET page doesn't work as expected: not async and isBusy is always false

I'm pretty new on ASP.NET but I've already used BackgroundWorkers on desktop applications.
This time I've created a simple ASP page that shows a button. When it's clicked, a 5 second operation is called and I want to set a graphical "loading" state. For example, I simply want to disable that button.
I tried this way:
private BackgroundWorker worker = new BackgroundWorker();
// ...
protected void confirmButton_Click(object sender, EventArgs e)
{
if (selectedAccount == null || worker.IsBusy) return;
worker = new BackgroundWorker();
worker.WorkerReportsProgress = false;
worker.WorkerSupportsCancellation = false;
worker.DoWork += (ss, ee) => {
// the operation
sync.StartOperation(selectedAccount);
};
worker.RunWorkerCompleted += (ss, ee) =>
{
// Operation finished, update the GUI
Report finalReport = sync.GetFinalReport();
if (finalReport.HasErrors())
{
ShowErrorMessage("Error");
}
else
{
ShowSuccessMessage("Completed");
}
SetLoadingState(false);
};
// before starting the worker, set a "loading" status ( => disable the button)
SetLoadingState(true);
// then start!
worker.RunWorkerAsync();
}
There are two problems here:
The GUI is not updated to the "loading" state (aka button doesn't get disabled). Seems like the operation is not really "async"...
Since I can still press the button during the operation, I tried to rely on the worker.isBusy value... but this property is always false, even if the operation is clearly running.
For the record, the last part where RunWorkerCompleted should be called works. The GUI is correctly updated with error or success messages.
What's wrong with that? Thanks.

c# - Backgroundworker refusing to do anything for another class

I have a WPF C# projects with the below buttonClick event void:
public void ButtonClick(object sender, RoutedEventArgs e)
{
_worker.WorkerReportsProgress = true;
_worker.DoWork += (o, ea) =>
{
try
{
_class1.hithere();
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
};
_worker.ProgressChanged += (o, ea) =>
{
};
_worker.RunWorkerCompleted += (o, ea) =>
{
MessageBox.Show("Done");
};
_worker.RunWorkerAsync();
}
I have a folder in the application called InformationProviders than contains the Class1.cs file and I have implemented the correct using MyApplication.InformationProviders; statement in the MainWindow.xaml.cs file that contains the button click event above.
I have also declared the Class1 class that is then called upon in the backgroundworker DoWork event correctly as such:
readonly Class1 _class1 = new Class1();
The Class1.cs file contains this little code made just to see if it worked and it doesn't unfortunately:
public class Class1
{
public void hithere()
{
MessageBox.Show("Hi, I'm working.");
}
}
What am I missing here???? I declared the class as public and (I believe) declared all that needed to be declared to make the process work...
All it does is display a message saying "Done", meaning it has completed the backgroundworker process (even though it did not do anything at all that was stated in the DoWork event. So pretty much, launching the worker and immediately considering it finished.
Regards and thanks,
Simon
Here's the tricky thing about running a multi-threaded application: only one thread has access to the UI and perform operations on it.
In case of your code, the BackgroudWorker in it's background operation attempts to show a message using MessageBox. This won't work - it's not being "fired" on the UI thread!
If you absolutely MUST perform UI operations from inside the BackgroundWorker (which you shouldn't do - this is what the ProgressChanged event is for) then you can use a Dispatcher class.
Here's a short example:
private void Button_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, a) =>
{
this.Dispatcher.Invoke(new Action(() => MessageBox.Show("doing stuff")));
};
bw.RunWorkerCompleted += (s, a) =>
{
MessageBox.Show("done");
};
bw.RunWorkerAsync();
}
Also fun fact, if you use Dispatcher.Invoke (as written above), then "doing stuff" will appear first, if you use Dispatcher.BeginInvoke then "done" will appear first, because the other operation will be queued on the UI thread.
Here's the "politically correct" way to use the BackgroundWorker:
bw.WorkerReportsProgress = true;
bw.DoWork += (s, a) =>
{
bw.ReportProgress(0, "doing stuff");
};
bw.ProgressChanged += (s, a) =>
{
MessageBox.Show(a.UserState as String);
};
I found the problem, i was using the Xceed.WPF.toolkit version of the MessageBox and it was just refusing to show that UI element in a backgroundWorker. Thanks for the help though it pointed me in the right direction. hoping this will help other people

The calling thread cannot access this object (rewrited but same error)

I have a MainFrame window with imageViewer control on it. Also there is my dll which calculates changes for the image all was working fine before I decided to add ProgressDialog.(( The Idea was - firstly I am loading the image via dll to main frame (this still OK). Then if user clicks button then show ProgressDialog and in worker.DoWork create new image via the same dllwrapper class (I am using "new")
All seems to be ok but when i am trying to set my currentImage property of imageviewer control (this is nothing more then setter for Image)it show me this error!
This is the code of my userButtonClickHandler from where I am launching ProgressDialog:
void OnThumbnailClick(object sender, RoutedEventArgs e)
{
pd = new ProgressDlg();
pd.Cancel += CancelProcess;
int max = 1000;
System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
//what process??
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
};
worker.RunWorkerAsync();
pd.ShowDialog();
}
There is function from the same MainFrame class for canceling (There is OK too)
void CancelProcess(object sender, EventArgs e)
{
worker.CancelAsync();
}
This is class for ProgressDlg (it has nothing more then progress bar and cancel button):
public partial class ProgressDlg : Window
{
public ProgressDlg()
{
InitializeComponent();
}
public string ProgressText
{
set
{
this.lblProgress.Content = value;
}
}
public int ProgressValue
{
set
{
this.progress.Value = value;
}
}
public event EventHandler Cancel = delegate { };
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Cancel(sender, e);
}
}
}
I am working with this problem for (almost) two days and still couldn't find the solution. Help me please if you have an idea.
1 UPDATE
It seems to me that you was right about this threads - when I am trying to load previously loaded(initial) image (from the main thread) -it loads OK but if I am trying libWrap it fails due to processes conflict!
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
imageViewer.Width = 1000;//work!
imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!
}
2 UPDATE
I have tried this construction OnThumbnailClick
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
{
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}
This caused same error/ Perhaps it will be correct to pass this value in MainThread (UI)? But I have no idea how.( I couldnot use serializers - becouse it is rapidly calling operation and this images are temporary/
WPF cannot alter items that were created on another thread.
So if you create an ImageViewer on one thread, you cannot alter it's properties on another thread.
Instead, use the Dispatcher, which is WPF's internal message queue for the main UI thread, to update your objects.
Or, use Henk's Answer to do your work on another thread, but return the result to the main thread so it can update your ImageViewer's properties
You need at least these changes:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{ ... } // handle error
else if (args.Cancelled)
{ ... } // handle Cancel
else
{
imageViewer.CurrentImage = args.Result;
}
pd.Close();
}
I'm not sure if it's enough but try again.
The imageViewer was created on the main thread of the application (which is appropriate because it is a UI control). UI controls can ONLY be accessed by the thread which created it, and that thread must have its own dispatcher (by which I mean message loop).
Remove the threading code, and it will work.
If you want this to popup the window and then show the image when the conversion completes, you will have to store the returned image in a variable until you return to the main thread, and then make the assignment to the imageViewer.

Altering the ObservableCollection according to FileSystemWatcher change notification

I'm trying to update my ObservableCollection as the FileSystemWatcher notifies changes. I know this is not possible because of cross thread operations.
So i would like to get the name of the file created/deleted/renamed when the event is fired and update it in the UI thread once the event is completed, as we do in BackgroundWorker. Can anyone tell me how to do this?
Also tell me where i should define and start this FileSystemWatcher. Currently i've defined it in the MainViewModel.
P.S.: I've seen similar questions in SO, but didn't get a clear picture
Thanks In Advance,
Veer
I would think the main view model is the right place to define the FileSystemWatcher. As for the threading issues, this is the easy way:
_watcher = new FileSystemWatcher(path);
_watcher.Created += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Created event
};
_watcher.Changed += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Changed event
};
_watcher.Renamed += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Renamed event
};
_watcher.Deleted += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Deleted event
};
// ...
_watcher.EnableRaisingEvents = true;
Each of the "Code to handle" will execute within the UI thread so it can update the ObservableCollection. Note that the FileSystemEventArgs "e" is available within this code.
If you prefer to use separate event handler methods you can call them from the above code or use this convenient shortcut:
var switchThread =
(FileSystemEventHandler handler) =>
(object obj, FileSystemEventArgs e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
handler(obj, e))
_watcher = new FileSystemWatcher(path);
_watcher.Created += switchThread(OnCreated);
_watcher.Changed += switchThread(OnChanged);
_watcher.Deleted += switchThread(OnDeleted);
_watcher.Renamed += switchThread(OnRenamed);
_watcher.EnableRaisingEvents = true;
where OnCreated, OnChanged, OnDeleted, and OnRenamed are normal event handler methods with the normal signature, for example:
void OnChanged(object sender, FileSystemEventArgs e)
{
// Code to handle Changed event
}
Personally I prefer the first way of doing it because I don't like creating four extra 1-line methods.
Note that your view model will need to know which Dispatcher to call back on. The easiest way to do this is to derive your view model from DispatcherObject, as assumed above. Another way is for the view model's constructor or the method that registers the FileSystemWatcher events to store a copy of Dispatcher.Current in a local field or local variable, then use that for the .BeginInvoke calls.
Also note that you can use exactly the same code in your view code-behind instead of in your view model if you prefer.
public void SomeActionToBeInvokedOnTheMainThread()
{
if (someControl.Dispatcher.CheckAccess())
{
// you can modify the control
}
else
{
someControl.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(SomeActionToBeInvokedOnTheMainThread)
);
}
}
I used Ray B.'s approach but had to modify things slightly and thought I'd post an update here to maybe save others some time.
My VS2010/.NET 4.0 WPF project was throwing the error:
Cannot assign lambda expression to an implicitly-typed local variable
After some tweaking I came up with the following. Note the additional var defined to handle the Renamed event:
var switchThreadForFsEvent = (Func<FileSystemEventHandler, FileSystemEventHandler>)(
(FileSystemEventHandler handler) =>
(object obj, FileSystemEventArgs e) =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
handler(obj, e))));
var switchThreadForFsRenameEvent = (Func<RenamedEventHandler, RenamedEventHandler>)(
(RenamedEventHandler handler) =>
(object obj, RenamedEventArgs e) =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
handler(obj, e))));
_fileSystemWatcher = new FileSystemWatcher(documentCollectionPath);
_fileSystemWatcher.Created += switchThreadForFsEvent(OnFileCreated);
_fileSystemWatcher.Deleted += switchThreadForFsEvent(OnFileDeleted);
_fileSystemWatcher.Renamed += switchThreadForFsRenameEvent(OnFileRenamed);
_fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
_fileSystemWatcher.IncludeSubdirectories = true;
_fileSystemWatcher.EnableRaisingEvents = true;

Categories

Resources