Properly use Background Worker in a WPF application - c#

i'm stuck in using properly the background worker, the app needs to communicate with the database, so it takes a bit longer and the UI freezes for a while, i need to create a function that does the database things and wait until the work is finished, for this time, i want to display a kind of window that inform the user about the app state (Loading, busy, Downloading).
My code
i didn't write any code yet, but here're what i need:
//instructions
InitializeComponent();
//do this in background and wait until it finnishes
GetEntitiesFromDatabase();
entitiesListView.ItemSource = someList; (GetEntitiesFromDatabase will initialize this list)
//....
How can i proceed, i know that this question might be already asked in the forum but i'm desperate by searching for an answer, if this can be done other way please help me, thanks in advance.

If you are going to directly manipulate the UI like that, you need to use Dispatcher.BeginInvoke when you aren't on the UI thread (read, in the BackgroundWorker).
Dispatcher.BeginInvoke(new Action(() =>
{
entitiesListView.ItemSource = someList;
}), null);
You could also bind that items source to your view model (a much better idea) and the framework will marshal the change to the bound property for you.
Basically, either use Dispatcher.BeginInvoke or switch to MVVM (which WPF was meant to use anyways).

Related

Really impossible to run animated GIF in async threads?

I want to run long lasting methods, even containing time consuming dlls, while a Spinner GIF is rotating.
OK, the most accepted solution is to run these methods in a BackgroundWorker while the GIF is shown in the main thread and I have already done, successfully, it but… but I am still curious to know if it is really impossible to do do the other way round !
This would simplify the calls to those methods, especially if they have arguments and return values, avoid the use of Invoke if they contain Labels to show the working progress, etc…
I have spent quite a lot of time browsing the Web but all the suggested solutions don’t work for me: as soon as the program calls my methods the Spinner stops rotating and resume working only when the methods end.
Ciao and thanks for any suggestion.
DONE!!
Thanks to the Camilo Terevinto sentence “You cannot do UI work on a non-UI thread” I asked myself if it was not possible to create the PictureBox that holds my spinning GIF in another thread and…
I created a new borderless Form (named frmSpinner) with inside a PictureBox running the Spinner.gif.
Then I used, in the main Form with the long running methods, a BackgroundWorker that, in the DoWork event, has a frmSpinner.Show().
Now the Spinner rotates endless without interrupting and resuming.
I have still to solve the problem how to place the Spinner Form on the right position on the main Form but, with the help of the PointToScreen method, it shouldn’t be to difficult.
Ciao and thanks for the suggestion.
Dealing explicitly with threads in a WPF/c# project is not canonical, unless you have something very specific to do, using Tasks is the modern way.
You can have a background task, started with for instance:
Task.Run(() =>
{
// blocking methods running outside UI thread
var newPropValue = //... update a UI property
Application.Current.Dispatcher.Invoke(() =>
{
//Running in UI thread.
PropValue = newPropValue;
});
});
With this pattern you can remove all the heavy/blocking work from the UI thread and have a flowing UI experience.

How to clear the text inside a TextBox when it is being passed down in a function?

Here is what my code looks like:
private void exportToExcelButton_Click(object sender, EventArgs e)
{
txtBox.Clear();
txtBox.AppendText("Beginning Export...");
ExportExcel(txtBox);
txtBox.AppendText("Export complete...");
}
The problem I am having is that whenever the button is clicked (to execute the function above), only part of the current text in the TextBox (System.Windows.Forms.TextBox) is cleared, and replaced with the first line: "Beginning Export ...".
However once the function ExportExcel(txtBox) is done executing, then the entire text is replaced by the new one generated in ExportExcel(txtBox).
Inside ExportExcel(txtBox); I have several txtBox.AppendText() statements explaining to the user the actions being made.
I have tried clearing the text with txtBox.Text = String.Empty; and txtBox.Text = "";and neither have worked.
Let me know if anything needs to be clarified, thanks.
Looks like you're blocking the GUI thread, preventing the text box from redrawing itself. This is one reason why you shouldn't perform long-running tasks on the GUI thread. Use a background thread instead. That way you leave the GUI thread free to perform important operations like drawing, responding to mouse clicks, etc.
Have you tried the textBox.Refresh , before calling txtBox.AppendText("Beginning Export...").
The method invalidates the control.
On the other hand, if you use a background thread, then you should update the UI only by overriding the Progress Changed event. Background threads are not meant for updating user interfaces. Try searching for Worker threads and UI threads. They correlate to MFC, but the concept is the same.
Also keep in mind the cross thread calls.
I agree with dvnrrs. However if you are unable to do this, try calling txtBox.Refresh();after adding each line of text.
There is another method called Application.DoEvents(); that has a similar behavior, but its use is not recommended since it sort of short-circuits the normal application flow and can cause your application to fail unexpectedly or do strange things.

Unreliable GUI update in multithreaded C# program

I have a program that runs a series of methods in other threads within one window and let's the user know what's going on using a status bar. The status bar updates are in the main thread which set's the status bar and then refreshes the GUI. There are several blocks of code in series each looking something like this:
Thread do1Thread = new Thread(Class.Method);
do1Thread.Start();
// inform user
this.status.Text = "Doing stuff 1...";
// update GUI
Utility.RefreshGUI();
// join thread
do1Thread.Join();
Sometimes the status bar does indeed update but often is stays on the first status until the end when it displays the last status. Occasionally is sticks on "Ready." which is the default.
Note that two of the blocks take a few seconds so there should be time for it to update. Also, the program is written in C# (Mono) using GTK# for the GUI.
How can I ensure that that the GUI updates to reflect the change?
The problem is that the Join() call blocks the UI thread which blocks all window messages.
Can you use a BackgroundWorker and execute whatever code you have after the Join in the RunWorkerCompleted call?
You need to dispatch Update message to UI thread, call invoke instead of direct property
this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen"));
The best way I have found to update a control in a primary thread is to set a delegate for updating and invoke that from other threads.
You have to use observe and observable pattern.
EDITED:
It is really better divide logic and view parts of code.
Here is an example in real world how to use. Pattern
Could you check whether you are using a StatusStrip control?
If so, your code looks like setting directly the Text of Stautus Strip Control
this.status.Text = "Doing stuff 1...";
So it wont reflect in the Status Strip as Text. You have to place a toolstriplabel and need to set its text in this case.
Please check the post here

Creating heavy user controls in WPF

I know that I cannot spawn different threads and put it in the UI thread to add it in the Visual Tree as it will throw an exception that it cannot access that object because a different thread owns it.
My current scenario is that I am heavily creating UI controls runtime, say like 200 (FrameworkContentElement) controls and add it to the DockWindow. Is it possible for me not to freeze the UI while creating this and try to load them to the UI thread? I cannot even show a progress dialog because that will use the UI thread while showing the dialog while doing work on another thread, that is okay if what I need to handle is data and put it in the UI but this time I need to create these UI controls.
One approach I've thought is create the UI controls and serialize them into MemoryStream and load them to the UI thread, one problem in here is that I have to re-attach the DataContext to the controls but that is fine, at that moment I can delegate it to another thread. Problem still is that is this feasible to do?
I tried mixing Task and Thread object to make the ApartmentState to STA but still no luck.
public static Task<T> StartSTATask<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
Thread thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
}
EDIT: These controls again are FrameworkContentElement, virtualizing controls in this scenario won't help. This is with FlowDocument controls creating the controls in runtime. Say, Runs, Tables, Paragraphs, etc.. Therefore, ListBox, TreeViews, etc are not applicable in this scenario.
200 controls shouldn't pose that big of a problem to render WPF on a decent machine can take a few thousand primitives.
You can show a progress bar while loading your data and while parsing it. Then you can throttle creating the UI elements if needed by having and off-UI-thread process loop over your data and call UI thread to instantiate controls. You can even separate instantiations by a small sleep to let the screen render, but only use this for VERY heavy UI...
... that being said - if your UI is so heavy you're probably designing it wrong. The question should not be
"how many UI elements can I put before my UI slows down to a drag?"
but
"what's the smallest number of active UI elements that can do the job?".
The word "active" refers to the approach taken by listviews where the actual items are virtualized - they are only created as needed and disposed if not visible. So instead of a DockPanel consider using a virtualizing container, such as a ListView, if your UI allows for it;
I can elaborate further if you can provide an example of your specific UI elements.

C# - Cross-thread operation - Create Control in thread, add to main form

I have an older form that I really don't want to rewrite at this point, so what I'm doing is loading the form and then adding it to a panel in the new UI form. This is working fine, but it's slow. The old form does a lot of loading and gathering of data and it's not very efficient. So as a result larger records take up to 30 seconds to load. As you know, creating the form then "locks up" the main UI for about 30 seconds while it loads the old form. This is the action I'm trying to prevent. I want to load the new form, display a "Loading" gif in the blank panel, and then once the old form is loaded remove the "Loading" image and add the form as a control.
And here starts the problem.
I've tried creating a Background Worker but this causes a STA error (old form has a few threaded data loadings of it's own), and since I can't change the worker to STA I stopped trying.
I've tried to create an Invoke (and BeginInvoke) and while this works, it doesn't really load the old form in the thread. It simply sends it back to the UI thread and does the work there. Again this hangs the UI. I.E.: Not what I want.
I've tried to create a delegate and trigger it as an event in the thread, but I get the same results as below...
I've created a thread, set STA on it, started it and then did a while loop with a DoEvents waiting on it to finish. Of course this all seems to work up to the point of accually adding the form to the panel, and then I get the "Control 'ChartForm' accesses from a thread other than the thread it was created on". In this error 'ChartForm' is the old chart that was loaded in the thread.
I've tried the above method, but I instead used a private static field to hold the creating of the old form, and then adding it to the panel once the thread is completed. This is in the method that created the thread, just after the while loop. Same error.
So, I've used the above method in other places with DataTables and didn't have any issue getting the data back to the main thread for use with DataBinding. I know that this is a little different but I didn't think that it would be this hard to do.
Below is the code that I have tried to use that seems to be the closest to what I want.
private static _ChartForm;
private void LoadPatientChart()
{
ClearMainPanel(); // Removes any loaded ChartForms from Panel
if (_Patient == null) // Test to make sure a patient is loaded
return;
loadingPanel.Visible = true; // Displays the "Loading" gif
Thread thread = new Thread(new ThreadStart(this.GetChartForm));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (thread.ThreadState != ThreadState.Stopped)
Application.DoEvents(); // Keeps the UI active and waits for the form to load
this.ChartPanel.Controls.Add(_ChartForm); // This is where the error is
loadingPanel.Visible = false; // Hide the "Loading" gif
}
private void GetChartForm()
{
ChartForm chartForm = new ChartForm(_Patient.AcctNum.ToString(), false);
chartForm.TopLevel = false;
chartForm.FormBorderStyle = FormBorderStyle.None;
chartForm.Dock = DockStyle.Fill;
chartForm.Visible = true;
_ChartForm = chartForm;
}
It's really not a good idea to create UI controls on any other thread than the UI thread. It is technically possible, but it's difficult to manage, especially if the new thread is a "temporary" one.
What you really need to do is refactor out the work that the ChartForm is doing (on construction it appears?) and do that work on a background thread, and then return it to your UI thread and then create your ChartForm passing in the results of that work. IMHO this is a better design anyways; although it may be a lot of work for you.
I don't think what you want is possible without refactoring this "old form". There is only one UI thread, and all UI elements must be created on that thread to be displayed to the user.
I would suggest refactoring the form to display initially without any data (or maybe with a loading image), and then have the form start a background task using BackgroundWorker to perform the long running tasks that are not UI related (going to a database, etc.) Once the worker is complete, then you can run the code that initializes the Form's data elements. This will keep the UI responsive for as long as possible while the blocking tasks are performed.
I've tried to create an Invoke (and BeginInvoke) and while this works,
it doesn't really load the old form in the thread. It simply sends it
back to the UI thread and does the work there. Again this hangs the
UI. I.E.: Not what I want.
You must update the user interface on the main thread, you do not have any choice, if its still hanging then your doing the calculations in the wrong thread.

Categories