4

I have a C# WPF application using a rather noddy MVVM approach. In one of the ViewModels I would like to run a sequence of tasks sequentially but would like to run each asynchronous to the main thread. I want the granularity of being able to report progress between tasks but I don't want to block the GUI while any of the tasks is running.

Is there a standard way of achieving this, or a "best practice"?

I have implemented something that makes use of BackgroundWorker that I feel at once happy and mildly horrified with. The code to kick the whole thing off feels especially non-C#ish. I feel there must be a better or, at least, an established way of doing this.

Many thanks for your suggestions.

Dan


Here's the cobbled-together option:

protected void runAsyncTask(SequentialTask seqTask)
    {
        if (HasErrored) return;

        DoWorkEventHandler worker = (s, e) =>
        {
            setTaskStartStatusMessage(seqTask.TaskMessage);
            ShowProgress = true;
            seqTask.Task((BackgroundWorker)s);
        };

        ProgressChangedEventHandler progress = (s, e) =>
        {
            if (seqTask.TaskProgress != null)
                seqTask.TaskProgress(e.ProgressPercentage, e.UserState);
        };

        RunWorkerCompletedEventHandler done = null;
        done = (s, e) =>
        {
            ShowProgress = false;
            if (e.Error != null)
            {
                HasErrored = true;
                displayTaskExceptionMessage(e.Error, seqTask.TaskMessage);
            }
            else
            {
                setTaskCompleteStatusMessage(seqTask.TaskMessage);
                if (seqTask.TaskComplete != null)
                    seqTask.TaskComplete();
            }
            ((BackgroundWorker)s).RunWorkerCompleted -= done;
            ((BackgroundWorker)s).DoWork -= worker;
            ((BackgroundWorker)s).ProgressChanged -= progress;

            if (seqTask.NextTask != null && (seqTask.CanExecuteNext == null ? true : seqTask.CanExecuteNext()))
                runAsyncTask(seqTask.NextTask);
        };

        if (seqTask.TaskProgress != null)
            backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.DoWork += worker;
        backgroundWorker.RunWorkerCompleted += done;
        backgroundWorker.ProgressChanged += progress;
        backgroundWorker.RunWorkerAsync();
    }

SequentialTask is just a simple set of Properties:

public class SequentialTask
{
    public Action<BackgroundWorker> Task { get; set; }
    public String TaskMessage { get; set; }
    public Func<bool> CanExecuteNext { get; set; }
    public Action<int, object> TaskProgress { get; set; }
    public Action TaskComplete { get; set; }
    public SequentialTask NextTask { get; set; }
}

Which leads to the Perl-like syntax of:

runAsyncTask(new SequentialTask()
        {
            Task = (x) => loadFile(),
            TaskMessage = "Load File",
            CanExecuteNext = null,
            NextTask = new SequentialTask()
            {
                Task = (x) => validateImport(),
                TaskMessage = "Validate Input Lines",
                TaskComplete = () =>
                {
                    if (!ImportIsValid)
                        displayValidationMessages();
                },
                CanExecuteNext = () => ImportIsValid,
                NextTask = new SequentialTask()
                {

etc.

2
  • Coroutines or Reactive Framework are a good fit for this: csharperimage.jeremylikness.com/2010/03/… Commented Nov 9, 2010 at 14:25
  • Hi Jay, thanks for the link - had a quick look over and it's something I'll definitely spend the time getting my head around but I think Adam's TPL suggestion is where I'll spend my initial efforts. Commented Nov 9, 2010 at 14:58

2 Answers 2

7

Have you looked at the Task Parallel Library (TPL) in .NET 4.0? This allows you to do things like this:

Task firstTask = new Task(()=>RunStepOne());
firstTask.ContinueWith(task=>()=>RunSecondStep());
firstTask.Start();

There are a ton of options for creating, continuing, and stopping tasks built on the TPL. It will certainly be worth a look.

Sign up to request clarification or add additional context in comments.

3 Comments

Not to mention that this will give you a leg up when C# 5 is released.
Thanks Adam, that's the next chapter in C# in a Nutshell - I'll get on and have a look. From the snippet you've shown it certainly looks a lot cleaner than chaining recursive structures.
I think that this definitely gives me what I want and in an "out-of-the-box", thanks again.
0

I don't see anything wrong with your approach. And the most important thing is: it works.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.