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

发布于 2024-10-14 11:22:13 字数 243 浏览 2 评论 0原文

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

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

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

转角预定愛 2024-10-21 11:22:13

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

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

这种机制很好,因为:

  1. 业务层独立于表示层中可能发生的事情。
  2. 很容易扩展双向通信。您可以轻松地更改事件,以便表示层(或任何其他订阅者)可以返回取消标志,以便业务层知道必须取消长时间运行的流程。
  3. 它允许同步或异步工作。也就是说,您可以在阻塞调用(即您的表示层和业务层共享同一线程)或非阻塞调用(即您的业务层使用后台工作线程)上使用它。 System.ComponentModel.BackgroundWorker 类可用于后一种情况,但如果你想引发多种类型的事件,那就不太好了。

希望这有帮助。

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:

  1. The business layer stays independent of what may go on up in the presentation layer.
  2. It is easy to extend for bi-directional communication. You can easily alter events so that the presentation layer (or any other subscriber) can return a cancel flag so that the business layer knows that a long-running process must be cancelled.
  3. It admits either synchronous or asynchronous work. That is, you can use it on blocking calls (i.e. your presentation and business layer share the same thread) or non-blocking calls (i.e. your business layer uses a background worker thread). The System.ComponentModel.BackgroundWorker class can be used in the latter case, but it's not too good if you want to raise multiple types of events.

Hope this helps.

扛刀软妹 2024-10-21 11:22:13

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

后台工作程序提供了在单独的线程上运行密集型操作所需的方法,并接收其进度的状态更新(通过 <代码>ReportProgress、ProgressChangedRunWorkerCompleted)。

实际上,我个人一直在尝试在 Web 环境中使用 BackgroundWorker 来运行计划任务。我决定在 Codeplex 上发布我迄今为止所做的工作。我觉得我的代码的精神可能对您的情况有用。 “Web 计划任务框架”codeplex 项目

如果您选择下载该项目,您将看到我如何在 ScheduledTaskRunner 类中使用 BackgroundWorker 类。我的实现没有将进度事件附加到工作人员,但这样做很容易。另外,我当前的实现重点是在给定的时间间隔内运行任务,但将其修改为更多的“按需”处理队列并不是很困难。现在我想到了这一点,我什至可以将其添加为一项功能:)

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

祝你好运,如果您对我的代码有任何疑问,请告诉我。

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.

故事和酒 2024-10-21 11:22:13

我对类似案例采取了以下方法。此视图的操作可能需要很长时间,我想定期显示进度。长时间运行的操作被推送到另一个类 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;
    }
}

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;
    }
}
世态炎凉 2024-10-21 11:22:13

根据您的其他评论,您正在努力保持业务层尽可能干净。

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

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

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

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

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)

失而复得 2024-10-21 11:22:13

您需要探索观察者模式(http://en.wikipedia.org/wiki/Observer_pattern)。对于桌面应用程序来说,这是非常常见的方法。对于 Web 来说,情况要复杂一些。您可能还想查看 Comet [http://en.wikipedia.org/wiki/Comet_(programming)] 以了解它是如何在 Web 上实现的。

You will need to explore Observer Pattern (http://en.wikipedia.org/wiki/Observer_pattern). It's pretty common approach for desktop applications. It's a bit more complex for the Web. You might also want to look into Comet [http://en.wikipedia.org/wiki/Comet_(programming)] to see how it's done for the Web.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文