Let's say we have an application that has a layered architecture. On the view we use a MVC or MVVM. The model is treated as the domain, it has a good part of the business logic.
Now let's say we have, in the model, a method that takes some time. A complicated calculation or a treatment that has to be done to each items of an object for example.
In the UI, we would like to display a progress bar and a text that would display the current step of the calculation (for example a listbox with all the process history).
How would you do that? How to send from the model the information of the progress of the process and how to hook up the Controller or ViewModel so that it will update the progress?
I often implement this in the following manner. My business-layer process, which takes a long time to run, raises events every so often to indicate that it's hitting specific "milestones". You decide what milestones to signal through events and how many of them. If your time-consuming process is a plain loop, you may choose, for example, to raise the same event again and again every 10% of the items in the loop. If it is a process with distinct phases, you may choose to raise a different event as each phase is completed.
Now, your presentation layer subscribes to those events and acts in consequence, updating the progress bar, the text or whatever.
This mechanism is good because:
Hope this helps.
I would recommend looking at the BackgroundWorker
class provided in the System.ComponentModel
namespace.
The background worker provides the methods you need to run your intensive operation on a separate thread, and receive status updates on it's progress (via ReportProgress
, ProgressChanged
and RunWorkerCompleted
).
I actually personally have been experimenting with using the BackgroundWorker
in a web environment, for the purpose of running scheduled tasks. I decided to publish the work I've done so far on codeplex. I feel that the spirit of my code could be useful for your situation. 'Web Scheduled Task Framework' codeplex project.
If you choose to download the project, you will see how I am using the BackgroundWorker
class in the ScheduledTaskRunner
class. My implementation does not attach progress events to the worker, but it would be very easy to do so. Also, my current implementation focuses around running a task on a given interval, but modifying it to be more of an 'on demand' processing queue would not be very difficult. I may even add that as a feature now that I think about it :)
Assuming you followed the approach of my code above, it would be easy to create an action on a controller of yours that went fired would inspect the list of 'tasks' (or a specific task you are interested in) and report the information as some sort of ActionResult
. Setup some javascript to poll the action on a specified interval and you'll have your progress!
Good luck and let me know if you have any questions about my code.
I took the following approach to a similar case. This view has an action on it that can take a long time and I would like to show progress periodically. The long running action is pushed down into another class, Worker. Some user action initiates the call to DoSomething
in TestViewModel.
TestView.xaml
...
<!-- Progress bar -->
<ProgressBar Visibility="Visible" Height="10" Value="{Binding SomeValue}"/>
...
TestViewModel.cs extends BaseViewModel, BaseViewModel just implements INotifyPropertyChanged
...
private void DoSomething(){
Worker worker = new Worker();
worker.ProgressChanged += new EventHandler<WorkerEventArgs>(OnProgressChanged);
worker.Start();
}
private void OnProgressChanged(object sender, WorkerEventArgs args){
SomeValue = args.Progress;
}
private const String SomeValuePropertyName = "SomeValue";
private double someValue;
public double SomeValue
{
get
{
return someValue;
}
set
{
if (someValue == value)
{
return;
}
someValue = value;
NotifyPropertyChanged(SomeValuePropertyName);
}
}
...
Worker.cs
...
public event EventHandler<WorkerEventArgs> ProgressChanged;
public void Start(){
//This will take a long time. Periodically call NotifyProgress
}
private void NotifyProgress()
{
if (ProgressChanged != null)
{
double progress = ...; //calculate progress
ProgressChanged(this, new WorkerEventArgs(progress));
}
}
...
WorkerEventArgs.cs
public class WorkerEventArgs : EventArgs
{
public double Progress { get; private set; }
public WorkerEventArgs(double progress)
{
Progress = progress;
}
}
Based on your other comments you are trying to keep your business layer as clean as possible.
Then the Model View ViewModel approach may fit: http://en.wikipedia.org/wiki/Model_View_ViewModel
As the calcualtion is done, you throw events that progress has been made.
These events are caught in the ViewModel, and the progress amount updated.
The View is then updated, due to databinding between the ViewModel and the View (Observer Pattern)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With