У меня есть приложение C # WPF, использующее довольно изворотливый подход MVVM. В одной из моделей представления ViewModels я хотел бы запускать последовательность задач последовательно, но хотел бы запускать каждую асинхронно с основным потоком. Мне нужна гранулярность, позволяющая сообщать о прогрессе между задачами, но я не хочу блокировать графический интерфейс, когда выполняется какая-либо из задач.
Существует ли стандартный способ достижения этого или "лучшая практика"?
Я реализовал кое-что, что использует BackgroundWorker
, и я чувствую себя счастливым и слегка испуганным. Код, с которого все начинается, чувствует себя особенно не на C #. Я чувствую, что должен быть лучший или, по крайней мере, устоявшийся способ сделать это.
Большое спасибо за ваши предложения.
Dan
Вот вариант с булыжником:
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
- это простой набор свойств:
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; }
}
Что приводит к Perl-подобному синтаксису:
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()
{
и т.д.