WPF - How to model a loop operation in the UI - wpf

I am creating a quick'n'dirty utility that will enable editing of data read sequentially from a set of files. Here's a very simplified explanation of what will happen in a single iteration of a loop:
Read a line from the input stream
Parse it and use the parsed results to populate form controls
Allow user editing and await a confirmation button click
Retrieve the updated form control values and write to the output stream
What I can't figure out is how to integrate the processing loop with the event-driven UI. For example, how do I suspend operation of the loop while waiting for user input.
I understand that this is possible by launching the loop operation on its own thread and writing code to manage its interaction with the UI thread, but I am wondering if there is a simpler approach that works out of the box.
Thanks for any ideas you may be able to offer.
Tim

I can think of 2 approaches to do that:
The easiest is probably to use a modal dialog: when your worker thread needs input from the user, display a dialog, which is a blocking operation. Something like that:
// Worker thread loop
while(...)
{
...
// prompt user for data (invoke synchronously on UI thread)
UserData data = (UserData)window.Invoke(PromptUserData);
...
}
...
UserData PromptUserData()
{
UserInputDialog dlg = new UserInputDialog();
dlg.ShowDialog();
return dlg.UserData;
}
The other option, if you don't want to use a modal dialog, is to use a wait handle to synchronize the worker thread and the UI:
private readonly AutoResetWaitHandle _userInputWaitHandle = new AutoResetWaitHandle(false);
...
// Worker thread loop
while(...)
{
...
// Setup the UI to allow user input
window.Invoke(SetupUIForInput);
// Wait for the input to be validated
_userInputWaitHandle.WaitOne();
...
}
...
void SetupUIForInput()
{
// Enable the UI
inputForm.Enabled = true;
// Whatever else you need to do...
...
}
void buttonOK_Click(object sender, EventArgs e)
{
// Signal the worker thread to continue
_userInputWaitHandle.Set();
}

You wouldn't really implement a loop in the narrow sense.
You would do something like this (pseudo code):
OnStartOperationButtonClick()
{
if(!ReadLine())
return;
ParseLineAndPopulateControls();
ShowEditingControls();
}
OnConfirmationButtonClick()
{
GetControlValuesAndWriteToOutputStream();
if(!ReadLine())
{
HideEditingControls();
return;
}
ParseLineAndPopulateControls();
}
No need for a separate thread, given that reading and parsing the line doesn't take long.

Related

ReactiveCommand in WPF/ReactiveUI app blocks UI thread

I am quite a beginner with ReactiveUI and have a strange behavior with a ReactiveCommand.
I want to query data from a database that currently does not support
asynchronous operations. Since we want to exchange the database in the
future with an asynchronous interface I want to write everything as
if the database already would allow async operations. As far as I understand
that would mean that I wrap my database calls at the lowest level in
a Task.
I have a button which is bound to a ReactiveCommand and the command
starts the database query. While the query lasts I want to show some
sort of animation.
The problem is that whatever I tried, the query blocks my UI thread.
Here is part of my code:
public ReactiveCommand<Unit, Unit> StartExportCommand { get; }
//The constructor of my view model
public ExportDataViewModel(IDataRepository dr)
{
this.dr = dr;
//...
StartExportCommand = ReactiveCommand.CreateFromTask(() => StartExport());
//...
}
private async Task StartExport()
{
try
{
Status = "Querying data from database...";
//Interestingly without this call the Status message would not even be shown!
//The delay seems to give the system the opportunity to at least update the
//label in the UI that is bound to "Status".
await Task.Delay(100);
//### This is the call that blocks the UI thread for several seconds ###
var result = await dr.GetValues();
//do something with result...
Status = "Successfully completed";
}
catch(Exception ex)
{
Status = "Failed!";
//do whatever else is necessary
}
}
//This is the GetValues method of the implementation of the IDataRepository.
//The dictionary maps measured values to measuring points but that should not matter here.
//ValuesDto is just some container for the values.
public Task<IDictionary<int, ValuesDto>> GetValues()
{
//...
return Task<IDictionary<int, ValuesDto>>.Factory.StartNew(() =>
{
//### here is where the blocking calls to the database
//### specific APIs take place
return result;
}, TaskCreationOptions.LongRunning);
}
I don't understand why this code is blocking the UI thread although I am wrapping
the long running query in a Task.
Is there something wrong with this pattern or should I go another way with Observables?
Edit 1
I am aware of the fact that async != threads. I thought, however, that Task with the TaskCreationOptions.LongRunning would make the blocking code run on a thread pool thread.
Edit 2
As recommended by Andy I set a breakpoint inside my task and had a look into the Debug Threads window. It tells me that Task is running on a worker thread. Still my UI is blocking.

Complex multi-threaded interface

First of all its not a splash-screen what i want... just to be clear... ok... lets go to the description of the problem:
i have a form that fire N number of threads (i dont know how many, the user must choose)... each thread has a object, and during several moments the objects may fire a event to signal some change... there must be a form for each thread to "report" the messages that the events are sending...
my problem is: the threads create the forms perfectally... but the desappear... out of nowhere... they appear on the screen... and vanish... poof.... gone! how can i avoid that undesired "disposing"?!?!
Your threads must either
use proper InvokeRequired + Invoke logic
or run their own MessagePump (Application.Run)
Which one did you (not) do?
If you create a form in a thread, the form will vanish when the thread is done. If you want the form to survive longer than that you need to either keep the thread alive, or create the form on the application's main thread. The latter would be preferable. Just make sure that each to hook up event listener for the object in the corresponding form, and use Invoke or BeginInvoke as needed when updating the form.
A simple example:
First a worker:
class Worker
{
public event EventHandler SomethingHappened;
protected void OnSomethingHappened(EventArgs e)
{
var evnt = SomethingHappened;
if (evnt != null)
{
evnt(this, e);
}
}
public void Work()
{
// do lots of work, occasionally calling
// OnSomethingHappened
}
}
Then, in a form we have an event handler for the SomethingHappened event:
public void SomethingHappenedHandler(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => SomethingHappenedHandler(sender, e)));
return;
}
// update gui here
}
Then it's really just a matter of wiring it all together:
Worker w = new Worker();
ProgressForm f = new ProgressForm;
w.SomethingHappened += f.SomethingHappenedHandler;
f.Show();
Thread t = new Thread(w.Work);
t.Start();
Disclaimer: this sample is quickly tossed together and somewhat untested (sitting on the train, about to get off ;) ).
A Form must be hosted on a thread with a message loop. You can create a message loop by either calling Application.Run or Form.ShowDialog. However, unless you have really good reason for doing so I would avoid having more than one thread with a windows message loop.
I would also avoid creating N threads. There are better ways to parallelize N operations other than creating one thread per operation. To name only two: 1) queue a work item in the ThreadPool or 2) use the Task Parallel Library via the Task class. The problem with creating N threads is that each thread consumes a certain amount of resources. More threads means more resources will be consumed and more context switching will occur. More is not always better in the world of multithreading.

The calling thread cannot access this object because a different thread owns it

i have a problem whenever i Refresh the prograss bar i get the error The calling thread cannot access this object because a different thread owns it
how can i remove it
shashank
backgroundWorker12 = new BackgroundWorker();
timer1.Enabled = true;
//cancel any async processes running for the background worker
//backgroundWorker1.CancelAsync();
backgroundWorker12.DoWork += (s, args) =>
{
BackgroundWorker worker2 = s as BackgroundWorker;
worker2.WorkerReportsProgress = true;
float percentageDone = 20f;
//check if the user status and update the password in xml
CheckUseridPwd();
//call the function to sync the wall chart data
//call the function to sync event relate data
percentageDone = 100f;
ValidateLogin2(txtUserID.Text.Trim(), txtPassword.Password.Trim(), -1);
worker2.ReportProgress((int)percentageDone);
};`
This bit looks like it's using UI controls from the wrong thread:
ValidateLogin2(txtUserID.Text.Trim(), txtPassword.Password.Trim(), -1);
I suggest you capture the user and password in local string variables above the code which adds the event handler - you can use those captured variables within your delegate. That way everything should be on the right thread:
backgroundWorker12 = new BackgroundWorker();
timer1.Enabled = true;
string user = txtUserID.Text.Trim();
string password = txtPassword.Password.Trim();
backgroundWorker12.DoWork += (s, args) =>
{
// ... same code as before up to here
ValidateLogin2(user, password, -1);
worker2.ReportProgress((int)percentageDone);
};
See if you can use the RunWorkerCompleted event of the BackgroundWorker, since you're accessing the UI only after progress is 100% i.e. done..
Then you wouldn't have to worry about thread-affinity of WPF UI controls - since the event handler is invoked again on the right/ UI Thread.
The other option (if you need to access the UI controls before the work is complete) is to cache the object
returned by Dispatcher.CurrentDispatcher on the UI Thread before the work starts and then use object.Invoke to marshal to the right thread from the thread pool thread that is executing your DoWork handler. See some code here.
Have you tried invoking ValidateLogin2
you can either do it directly from your code shown, or in ValidateLogin2 check if the method itself requires invoking. If not, go ahead and validate, but if it does, then have it invoke itself
void ValidateLogin2(...)
{
if (this.InvokeRequired)
{
//Invokes itself if required
BeginInvoke(new MethodInvoker(delegate(){ValidateLogin2(...);}));
}
else
{
//validate login here
}
}

Updating a Progress Bar from Another Thread

I have a windows form on the main thread and another thread that does some calculations. I'd like to update the status bar on my form from the work being done in the other thread. What's the best way to do this?
So far everything I've tried just makes things run really slowly. I'm using Visual Studio 2005.
You can use the marshaling techniques like Control.Invoke to execute a delegate on the UI thread where UI elements can be safely manipulated, but that approach is not very good. Actually, it is a terrible approach if all you want to do is update simple progress information.
By far the best method for doing this is:
Have your worker thread publish progress information to a shared variable.
Have your UI thread poll for it via a System.Windows.Forms.Timers on an interval that works well for you.
Here is what it might look like.
public class Example : Form
{
private volatile int percentComplete = 0;
private void StartThreadButton_Click(object sender, EventArgs args)
{
StatusBarUpdateTimer.Enabled = true;
new Thread(
() =>
{
for (int i = 1; i <= 100; i++)
{
DoSomeWork();
percentComplete = i;
}
}).Start();
}
private void StatusBarUpdateTimer_Tick(object sender, EventArgs args)
{
yourStatusBarPanel.Text = percentComplete.ToString() + "%";
StatusBarUpdateTimer.Enabled = percentComplete < 100;
}
}
This works well because:
The percentComplete field is declared 'volatile' ensuring its value can be reliably read from multiple threads.
The UI thread gets to dictate when and how often the UI gets updated...the way it should be!
The worker thread does not have to wait for a response from the UI thread before it can proceed as would be the case with Invoke.
It breaks the tight coupling between the UI and worker threads that Invoke would impose.
It is more efficient...considerably.
You get more throughput on both the UI and worker threads.
There is no chance of saturating the UI message queue as could be the case with BeginInvoke.
You do not have to litter you code with Invoke calls everytime you need to update the UI from the worker thread.
Make sure that you only update the user interface from the main thread or else you will have problems. You can switch your thread context by calling Invoke. There's a good post here on that.
You can send messages to the main thread and get it to update the progress bar, although you then need to check for the messages. You could also do the same sort of thing as a polling function.

Update Label while processing in Windows Forms

What is the best way to update a label on a Windows Forms application while processing?
I have a loop that does some processing to files on the user's system when the user clicks a button.
foreach (System.IO.FileInfo f in dir.GetFiles("*.txt"))
{
// Do processing
// Show progress bar
// Update Label on Form, "f.Name is done processing, now processing..."
}
What would be some sample code?
What exactly is this called? Is it threading or delegates?
A quick fix for you would be:
Label1.Text = f.Name + " is done processing, now processing...";
Label1.Refresh();
You really want to avoid DoEvents, otherwise you'll have problems if your user repeatedly presses buttons on your form.
You should be doing this on another thread, and then updating your UI thread from that thread. You are blocking further processing by performing this work on the UI thread.
If you can't move this code to the UI thread, then you could always call Application.DoEvents, but I strongly suggest you explore these options first:
System.ComponentModel.BackgroundWorker
System.Threading.ThreadPool
System.Threading.Thread
System.Threading.Tasks namespace
You'll need to get your data from one thread to the other. This can be done in a couple of ways...
First, your "background" thread could update some kind of "CurrentStatus" string variable that it changes as it goes along. You could then put a timer on your form that would then grab the CurrentStatus variable and update the label with it.
Second, you could simply invoke the operation from the background thread to the UI thread with a delegate using the InvokeRequired property of the label control. So for example...
private delegate void UpdateStatusDelegate(string status);
private void UpdateStatus(string status)
{
if (this.label1.InvokeRequired)
{
this.Invoke(new UpdateStatusDelegate(this.UpdateStatus), new object[] { status });
return;
}
this.label1.Text = status;
}
You can call that UpdateStatus() method from any thread (UI or background), and it will detect whether or not it needs to invoke the operation on the main UI thread (and if so, does it).
To actually set up the thread, you can do so like this:
private void StartProcessing()
{
System.Threading.Thread procThread = new System.Threading.Thread(this.Process);
procThread.Start();
}
private void Process() // This is the actual method of the thread
{
foreach (System.IO.FileInfo f in dir.GetFiles("*.txt"))
{
// Do processing
// Show progress bar
// Update Label on Form, "f.Name is done processing, now processing..."
UpdateStatus("Processing " + f.Name + "...");
}
}
Then when the user clicks the "GO" button you'll simply call StartProcessing().
If your processing is lengthy do it in a backgroundworker thread.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
I also recommend to use :
Application.DoEvents();
Processes all Windows messages currently in the message queue.

Resources