如何从业务/模型类发送进度更新?

时间:2011-01-25 11:01:49

标签: model-view-controller architecture mvvm

假设我们有一个具有分层架构的应用程序。在视图中我们使用MVC或MVVM。该模型被视为域,它具有很好的业务逻辑。

现在让我们假设我们在模型中有一个需要一些时间的方法。例如,必须对对象的每个项目进行复杂的计算或处理。

在UI中,我们希望显示一个进度条和一个显示当前计算步骤的文本(例如包含所有过程历史记录的列表框)。

你会怎么做?如何从模型中发送进程进度信息以及如何连接Controller或ViewModel以便更新进度?

5 个答案:

答案 0 :(得分:6)

我经常以下面的方式实现这一点。我的业务层流程需要很长时间才能运行,每隔一段时间就会引发事件,以表明它正在达到特定的“里程碑”。您可以决定通过事件发出信号的里程碑以及有多少里程碑。如果您的耗时过程是一个简单的循环,您可以选择,例如,循环中每10%的项目一次又一次地引发相同的事件。如果它是具有不同阶段的过程,您可以选择在每个阶段完成时引发不同的事件。

现在,您的表示层会订阅这些事件并执行相应的操作,更新进度条,文本或其他内容。

这种机制很好,因为:

  1. 业务层与表示层中的内容保持独立。
  2. 很容易扩展以进行双向通信。您可以轻松地更改事件,以便表示层(或任何其他订户)可以返回取消标记,以便业务层知道必须取消长时间运行的进程。
  3. 它承认同步或异步工作。也就是说,您可以在阻止调用(即您的演示文稿和业务层共享同一个线程)或非阻塞调用(即您的业务层使用后台工作线程)上使用它。 System.ComponentModel.BackgroundWorker类可用于后一种情况,但如果你想提出多种类型的事件则不太好。
  4. 希望这有帮助。

答案 1 :(得分:2)

我建议您查看BackgroundWorker命名空间中提供的System.ComponentModel类。

后台工作程序提供在单独的线程上运行密集型操作所需的方法,并接收有关其进度的状态更新(通过ReportProgressProgressChangedRunWorkerCompleted)。< / p>

实际上,我个人一直在尝试在Web环境中使用BackgroundWorker,以便运行计划任务。我决定发布我迄今为止在codeplex上所做的工作。我觉得我的代码精神对你的情况很有用。 'Web Scheduled Task Framework' codeplex project

如果您选择下载项目,您将看到我如何使用BackgroundWorker课程中的ScheduledTaskRunner课程。我的实现没有将进度事件附加到工作者,但这样做很容易。此外,我当前的实现侧重于在给定间隔上运行任务,但将其修改为更多“按需”处理队列并不是非常困难。我甚至可以将其作为一个功能添加到现在我考虑它:)

假设你按照我上面的代码的方法,很容易在你的控制器上创建一个被触发的动作来检查“任务”列表(或你感兴趣的特定任务)并报告信息作为某种ActionResult。设置一些javascript以在指定的时间间隔内轮询该操作,您将获得进度!

祝您好运,如果您对我的代码有任何疑问,请与我联系。

答案 2 :(得分:1)

我对以下类似的案例采取了以下方法。这个视图有一个动作,可能需要很长时间,我想定期显示进度。长时间运行的操作被推送到另一个类Worker。某些用户操作会在TestViewModel中启动对DoSomething的调用。

TestView.xaml

...
<!-- Progress bar -->
<ProgressBar Visibility="Visible" Height="10" Value="{Binding SomeValue}"/>
...

TestViewModel.cs扩展了BaseViewModel,BaseViewModel只实现了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;
    }
}

答案 3 :(得分:1)

根据您的其他评论,您尝试尽可能保持业务层的清洁。

然后,模型视图ViewModel方法可能适合:http://en.wikipedia.org/wiki/Model_View_ViewModel

计算完成后,您将抛出已取得进展的事件。

这些事件在ViewModel中捕获,并且更新了进度。

由于ViewModel和View(观察者模式)之间的数据绑定,视图随后被更新

答案 4 :(得分:0)

您需要探索观察者模式(http://en.wikipedia.org/wiki/Observer_pattern)。这是桌面应用程序的常用方法。它对Web来说有点复杂。您可能还想查看Comet [http://en.wikipedia.org/wiki/Comet_(programming)],看看它是如何为网络完成的。