Access windows control from Backgroundworker DoWork - winforms

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

Related

Avoid frequent updates in WPF custom control

I am writing a custom control in WPF. The control have several properties that cause update of the control's logical tree. There are several methods of this form:
private static void OnXXXPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((MyControl)obj).RebuildTree();
}
Suppose the RebuildTree() method is very complex and lenghty and if users changes several properties, this method is called several times causing application slowdown and hanging.
I would like to introduce something like BeginUpdate() and EndUpdate() methods in a Windows Forms fashion (to ensure the update is called just once), but this practice is widely disouraged in WPF.
I know the renderer have lower priority and flicker may not appear, but still why to spoil precious running time by calling the same update method multiple times?
Is there any official best practice on how to make efficient update of multiple dependency properties (without updating the entire control after setting each one)?
Just set a flag when any of these properties change, and have the refresh method queued to the Dispatcher only once.
private static void OnXXXPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((MyControl)obj).NeedsRefresh = true;
((MyControl)obj).OnNeedsRefresh();
}
void OnNeedsRefresh()
{
Dispatcher.BeginInvoke((Action)(() =>
{
if (NeedsRefresh)
{
NeedsRefresh = false;
RebuildTree();
}
}),DispatcherPriority.ContextIdle);
}
This way, all your properties will be updated and THEN the Dispatcher will call your BeginInvoke, set the flag to false and refresh only once.

performing database operations in the background in WPF application?

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.

Xaml parsing and multithreading

I'd like to load Xaml from code running in a background thread.
I understand I would have to sync with the dispatcher. However, it fails (throws an exception).
Why?
Here is the code
public MainWindow()
{
InitializeComponent();
Thread thread = new Thread(new ThreadStart(delegate
{
Dispatcher.Invoke(new Action(delegate
{
Content = XamlReader.Parse(
"<Button xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
Content='Hello World'/>");
}));
}));
thread.Start();
}
As Pavlo mentioned, you need to also set your content within the Dispatcher.
However, I will say - this is fairly useless.
Remember, when you call Dispatcher.Invoke or BeginInvoke, you're explicitly saying to run that code on the UI thread. By starting a background thread that does nothing but invoke back to the UI thread, you're effectively doing the same work on the UI thread, with the disadvantage of extra overhead being added to the system as well as harder debugging. In this case, you should just load the file directly.
This seems like a bad idea for a couple reasons. Are you expecting to get XAML fragments from a database or some other storage and you can't create the instances of these controls in C#? You could just create a Button directly.
Are you able to allow the data to drive your visualization? A good example of this is having a collection of ICommand objects (CommandViewModel, RelayCommand, etc) and a CommandView that you want to use to represent your command? In this case, it could be a <Button> with a binding to the CommandViewModel Title or Content property.
You're background thread could then be used to drive the population of data (collections, properties) and you're UI would be designed to flexibly accommodate the expected data patterns.

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.

In a WPF app, is there a object I can assign to FileSystemWatcher.SynchronizingObject?

In a WPF app, is there a object I can assign to FileSystemWatcher.SynchronizingObject?
I can make my own, but if there is one available, I would like to use it.
Reflector shows that the only class that implements ISynchronizeInvoke (i.e., the type of the FileSystemWatcher.SynchronizingObject property) is System.Windows.Form.Control (and its subclasses); there do not appear to be any WPF objects that implement this interface.
You need to create an ISynchronizeInvoke object that wraps the System.Windows.Threading.Dispatcher instance from the Window. That class is the closest thing WPF has to an ISynchronizeInvoke object.
Inside the wrapper class, simply forward the BeginInvoke call to the dispatcher you've wrapped. I did a bit extra work and also wrapped the DispatcherOperation that results from the Dispatcher.BeginInvoke method to call its Wait method inside the ISynchronizeInvoke.EndInvoke method.
Everything seems to be working correctly so far, it's too bad Microsoft didn't see fit to have the Dispatcher class implement the interface for ease of use.
There is one way. FileSystemWatcher when you enabling events (EnableRaisingEvents = true) creates it's own thread to monitor the FS events. Via ISynchronizeInvoke it can async invoke members of your Form for example, (it's thread can async interact with the main thread - UI thread).
In WPF there's no implementation of ISynchronizeInvoke, but there is a possibility to
interact with the UI thread, via Dispatched property of your Window like this:
var fsw = new FileSystemWatcher()
{
//Setting the properties: Path, Filter, NotifyFilter, etc.
};
fsw.Created += (sender, e) =>
{
Dispatcher.Invoke(new Action<params_types>((params_identifiers) =>
{
//here the code wich interacts with your IU elements
}), here_params);
};
//... in this way (via Dispatcher.Invoke) with the rest of events
fsw.EnableRaisingEvents = true;
Use the DispatcherTimer rather than the system timer.
This will work fine for WPF.
DispatcherTimer t1 = new DispatcherTimer();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
t1.Interval = new TimeSpan(0,0,0,0,200);
t1.Tick += new EventHandler(t1_Tick);
t1.Start();
...
void t1_Tick(object sender, EventArgs e)
{
//some work
}

Resources