performing database operations in the background in WPF application? - wpf

In my WPF application I need to perform database operattions in the background to make the UI more responsive.
I'm making use of the BackgroungWorker class, but since it operates on different thread than that of UI, i can't pass the parameters to the database queries.These values comes from the UI controls.
Could anyone please help me with this problem or suggest some other way to do database operations,at the same time making UI responsive.
Thank You

Instead of using BackgroungWorker class you could work with Dispatcher.BeginInvoke method. In fact as specified by MSDN:
BeginInvoke is asynchronous; therefore, control returns immediately to the calling object after it is called.
In WPF, only the thread that created a DispatcherObject may access that object. For example, a background thread that is spun off from the main UI thread cannot update the contents of a Button that was created on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the event queue of the Dispatcher at the specified DispatcherPriority.
Here a good article that explains how to work with Dispatcher class.

I think the BackgroundWorker is the correct tool for the job. When you create a BackgroundWorker you specify an event handler for the DoWork event. The DoWorkEventArgs object has a property on it called Arguments which is the object passed in when you start the BackgroundWorker by calling RunWorkerAsync. You may need to create a helper class to handle the parameters you need to pass, but that should be quite easy. Something like
Helper Class:
public class WorkerArgs
{
public string Arg1 {get;set;}
public object Arg2 {get;set;}
public int Arg3 {get;set;}
}
Background Worker:
BackgroundWorker worker = new BackgroundWorker();
// Hook up DoWork event handler
worker.DoWork += (sender, e) => {
WorkerArgs args = e.Arguments as WorkerArgs;
// ... Do the rest of your background work
};
// Create arguments to pass to BackgroundWorker
WorkerArgs myWorkerArgs = new WorkerArgs {Arg1 = "Foo", Arg2 = new Object(), Arg3 = 123 };
// Start BackgroundWorker with arguments
worker.RunWorkerAsync(myWorkerArgs);
In your case, you would populate the helper class object with values from your UI controls.

Related

"BindingSource cannot be its own data source" - error when trying to reset the binding source from a method in another class

We are binding a DataGridview using BindingSource. So in the main thread we have given like this.
class1BindingSource = new BindingSource();
class1BindingSource.DataSource = class1List;
this.dataGridView1.DataSource = class1BindingSource;
After that i have a placed a background worker in the form and is triggering in a button click.
i.e. in the button click
this.backgroundWorker1.RunWorkerAsync()
In the BackgroundWorker DoWork Event i am trying to update the BindingSource and there by trying to update the DataGridview.
So the BindingSource reset is done in a method in another class.
DoWork Event
Class2 cl2 = new Class2();
cl2.UpdateBindingSource(class1BindingSource);
UpdateBindingSource Method
public void UpdateBindingSource(BindingSource bs)
{
Class1 c1 = bs.Current as Class1;
for (int i = 0; i < 1000; i++)
{
lock (bs.SyncRoot)
{
c1.MyProperty1 = i;
bs.ResetItem(0);
}
}
}
Now i am getting an exception like BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refer back to BindingSource.
If i am doing this in my DoWork Event then i can reset the item in the control thread itself using BeginInvoke method.
But actually i am trying to simulate our application scenario. So i want to solve this in this format.
Can any one help me on this.
The problem is that you can't update a BindingSource within a thread other than the gui thread. This is due the fact, that the BindingSource will fire some events which will then be received by your data grid view which will then start to update itself, which will fail cause it won't be done on the gui thread.
So right before you call RunWorkerAsync() you should call class1BindingSource.SuspendBinding() and within your RunWorkerCompleted you should call class1BindingSource.ResumeBinding().
Also ensure that within your DoWork you won't call any methods on the binding source (like you did with bs.ResetItem(0)).
And also remove this lock statement. It simply doesn't make any sense (in your example) and if you really need it (in your real code) consider using some private object _Gate = new Object(); within your class to avoid any deadlocks from the outer world, cause bs.SyncRoot is publicly available.
I had the same problem:
- BindingSource that had elements with INotifyPropertyChanged
- A separate Task that updated the elements.
The suggested solutions SuspendBinding etc didn't work. BindingSource should have done something like IsInvokeRequired.
Luckily Ivan Stoev came with the brilliant idea of subclassing the BindingSource and do something similar as IsInvokeRequired. Thank you Ivan!
Link: Update BindingSource from a different Task
UpdateBindingSource() does not take much time, so no need to use backgroundworker. You can invoke UpdateBindingSource() in the main thread.
Also, keep datagridview manipulation in the main thread.

Updating a TreeView ItemsSource in another thread

I have a function like this:
public void UpdateList()
{
BindList = Model.GetList();
TreeView1.ItemsSource = BindList;
}
UpdateList() needs to be called (and the tree view list therefore reloaded) every time the user performs some action. It currently works, it's just very slow so I want to do it in another thread.
I am quite new to WPF... on WinForms programs I used to make delegates and check if InvokeRequired, etc. But I have found this does not work in WPF.
So if I want to call UpdateList() such that it runs in another thread, and the ItemsSource is updated from that other thread, how should I do it?
Thanks
Change Model.GetList() function to property of type ObservableCollection<> For example:
public ObservableCollection<DataItem> List { get; set; }
Call TreeView1.ItemsSource = Model.List only once. It gives you advantages of WPF binding.
Create a BackgroundWorker to load the data in DoWork handler. Load data to temporary collection and copy this collection to Model.List at RunWorkerCompleted handler.
If you want to update your tree while data is loading you may look at this approach: WPF update binding in a background thread
Read and learn MVVM pattern to understand the main idea of WPF developing

wpf BackgroundWorker - Regarding updating UI

I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.

Access windows control from Backgroundworker DoWork

my issue is the following:
I have a windows form in which I've placed a LayoutPanel, when the forms Loads, multiple controls like: textboxes and labels are being added to the LayoutPanel.
Then on a button click, I need to process the data entered by the user on those dynamically created controls. For that purpouse I use a Backgroundworker which is supposed to take those controls and read their data.
My issue es that the Backgroundworker doesn't allows me to access the control from the DoWork Method, but I need to do it that way because I'll be reporting the progress of the operations.
Here are portions of my code to clarify the concept:
private void frmMyForm_Load(object sender, EventArgs e)
{
//I add multiple controls, this one is just for example
LayoutPanel1.add(TextBox1);
....
}
private void bgwBackground_DoWork(object sender, DoWorkEventArgs e)
{
foreach (Control controlOut in LayoutPanel1.Controls)
{
//do some stuff, this one is just for example
string myString = controlOut.Name; //-> Here is the error, cant access controls from different Thread.
}
}
Setting text is simple just using a delegate, but how about getting the entire parent control to manipulate the child controls (just for getting info, I don't want to set any data, just need to Get Name, Text, stuff like that).
Hope I made myself clear, thank you all.
You can only access Windows Forms controls from the GUI thread. If you want to manipulate them from another thread, you will need to use the Control.Invoke method to pass in a delegate to execute on the GUI thread. In your situation, you should be able to do this:
private void bgwBackground_DoWork(object sender, DoWorkEventArgs e)
{
foreach (Control controlOut in LayoutPanel1.Controls)
{
this.Invoke(new MethodInvoker(delegate {
// Execute the following code on the GUI thread.
string myString = controlOut.Name;
}));
}
}
I like to define an extension method that allows me to use the cleaner lambda syntax:
// Extension method.
internal static void Invoke(this Control control, Action action) {
control.Invoke(action);
}
// Code elsewhere.
this.Invoke(() => {
string myString = controlOut.Name;
});
As you are already aware, accessing control values from any thread other than the UI thread is a big no-no. I'd say one reasonable implementation is to use a .NET synchronization mechanism, such as a WaitHandle, to suspend your background thread while the UI thread updates a thread-safe data structure of your choice.
The idea is that your background thread notifies the UI thread (via the delegate mechanism you are already familiar with) that it needs information, then waits. When the UI is finished populating the shared variable with information, it resets the WaitHandle, and the background worker resumes.
Without writing out and testing all the code, let me give you a few resources:
WaitHandle.WaitOne documentation with example usage: http://msdn.microsoft.com/en-us/library/kzy257t0.aspx
My own favorite method of invoking an event on the UI thread: http://www.notesoncode.com/articles/2009/01/24/PowerfulExtensionMethodsPart1.aspx

What is the use of a Dispatcher Object in WPF?

What is the use of a Dispatcher Object in WPF?
Almost every WPF element has thread affinity. This means that access to such an element should be made only from the thread that created the element.
In order to do so, every element that requires thread affinity is derived, eventually, from DispatcherObject class. This class provides a property named Dispatcher that returns the Dispatcher object associated with the WPF element.
The Dispatcher class is used to perform work on its attached thread. It has a queue of work items and it is in charge of executing the work items on the dispatcher thread.
You can find on the following link some more details on the subject:
https://www.codeproject.com/Articles/101423/WPF-Inside-Out-Dispatcher
A dispatcher is often used to invoke calls on another thread. An example would be if you have a background thread working, and you need to update the UI thread, you would need a dispatcher to do it.
In my experience we use Prism Event Aggregator. When the event happens it calls the Dispatcher.Invoke() to update the UI. This is because only the Dispatcher can update the objects in your UI from a non-UI thread.
public PaginatedObservableCollection<OrderItems> Orders { get; } = new PaginatedObservableCollection<OrderItems>(20);
_eventAggregator.GetEvent<OrderEvent>().Subscribe(orders =>
{
MainDispatcher.Invoke(() => AddOrders(orders));
});
private void AddOrders(List<OrderItems> orders)
{
foreach (OrderItems item in orders)
Orders.Add(item);
}

Resources