【问题标题】:WPF MVVM Report a progress from another class?WPF MVVM 报告另一个类的进度?
【发布时间】:2020-06-25 08:01:49
【问题描述】:

我正在使用 C# WPF 开发一个应用程序,要求是使用 MVVM 模式,但我正在努力寻找有关如何向进度条报告进度的信息,该进度条的进度值来自另一个类,我我会尝试用代码更好地解释自己。

MainWindowVieModel.cs

//...
//...
public void StartSearch(object obj)
{
  search.SearchByName(string a); //CALL TO THE METHOD IN THE SEARCH CLASS
}
//...
//...

Search.cs

public string SearchByName(string searchString){
//..
//..
    for (int i = 0;i <x;i++)
    {
        //HERE I NEED TO REPORT I AS PROGRESS VALUE
    }
}

MainWindow.xaml.cs 我将 MainWindowViewModel 绑定为 DataContext。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel();
    }
}

这甚至可以实现我正在寻找的东西吗?

【问题讨论】:

  • 在 Search 类中公开一个事件,然后在 SearchByName 循环中触发它。侦听器(由您决定)应该订阅该事件,然后处理一些图形。如有必要,还要考虑取消订阅事件。
  • @MarioVernari con 你提供了一个基本示例以便更好地理解?
  • 所提出的答案正是我的意思。
  • 您还可以将 IProgress 参数添加到 SearchByName 方法。对我来说,这比方法和事件的组合更容易使用和重用。

标签: c# wpf mvvm


【解决方案1】:

这当然是可能的。您需要做的是在您的 Search 类中设置一个事件:

public event Action<int> ProgressChanged;

然后设置要在循环内引发的事件:

public string SearchByName(string searchString){
    for (int i = 0; I < x; i++)
    {
        //...
        ProgressChanged?.Invoke(i);
    }
}

然后在您的MainWindowVieModel 中订阅该事件。这样做的好地方是你的构造函数:

public class MainWindowVieModel()
{
    Search.ProgressChanged += UpdateValueInProgressBar(i);
}

最后,你必须让你的 StartSearch 方法 async 不会冻结你的 UI:

public async void StartSearch(object obj)
{
  await Task.Run(() => search.SearchByName(string a);
}

请注意,您实际上并不需要 UpdateValueInProgressBar(),我只是让您决定如何在您自己的类中完成与 XAML 的绑定。

如果您有任何问题,请尽管提问!希望对您有所帮助。

编辑:请注意,正如@Mario Vernari 评论的那样,取消订阅该事件是防止内存泄漏的好主意。您必须决定在您的应用程序中执行此操作的位置。一种可能的解决方案是在方法本身中进行:

public async void StartSearch(object obj)
{
  Search.ProgressChanged += UpdateValueInProgressBar();
  await Task.Run(() => search.SearchByName(string a);
  Search.ProgressChanged -= UpdateValueInProgressBar();
}

编辑:您请求了UpdateValueInProgressBar 方法的示例。我假设您的 MainWindowVieModel 向您的视图公开了 ProgressMax 属性:

public class MainWindowVieModel
{
        private int _Progress;
        public int Progress
        {
            get => _Progress;
            set { _Progress = value; NotifyPropertyChanged(); }
        }
}

然后您在 XAML 中绑定到该属性:

<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress}" />

当然,这只是一个例子。在您的应用程序中,您还需要一种方法来了解您期望的 i 的最大值是多少(这里设置为 100)。 这些答案真的是为了给你一点方向。您可以自行解决具体情况。

【讨论】:

  • 赞成,因为这也是我的想法。但是,您应该通过取消订阅事件来完成代码。我们不知道原始代码是怎样的,并且坚持订阅事件可能会导致内存泄漏。
  • 感谢您的评论,好点子。您会在致电await Task.Run(() =&gt; search.SearchByName(string a); 之前订阅它,然后立即取消订阅吗?
  • 我认为由作者决定在哪里取消订阅,因为它取决于原始代码。只需添加 sn-p,并强调取消订阅很重要这一事实。
  • 如果你正在使用一个事件,它应该是一个弱事件,除非 Search.cs 由 mainwindowviewmodel 实例化。在这种情况下,不需要事件。
  • @yosef-bernal 感谢您的重播,请添加 UpdateValueInProgressBar 方法的声明?
【解决方案2】:

没有足够的信息,因为问题是要知道什么是“最佳”解决方案。

一个事件意味着一个强引用,由于存在内存泄漏的风险,这通常被认为是一个坏主意。 弱事件旨在避免这种情况,但并不那么简单。

还有一些其他的:

Mvvmlight 信使

https://social.technet.microsoft.com/wiki/contents/articles/26070.communicating-between-classes.aspx 或 prism 事件聚合器。两者都使用弱事件。 Prism 涉及传递对桥接对象的引用。

绑定值

您可以将 SearchVM 实例公开为 mainviewmodel 中的属性或 mainwindowviewmodel 中的属性,并将其绑定到进度条上的 Value。在 SearchVM 中设置它,直接或间接地将操作传递给 searchvm 的搜索。也许拥有一个单独的搜索类有点毫无意义。我们不知道。

自定义路由事件

根据调用搜索的位置,可能会有一些用户控件。这可以引发路由事件,并且此进度条的父级可以收听此事件。驱动路由事件将是用户控件的依赖属性。不过,我们不知道封装和可重用是否值得。

通过依赖注入传递引用

这是绑定的一种变体。

mainwindowviewmodel 的属性可以是progressVM.cs。这可能是通过 DI 提供的单例。在任何给定的时间点,可能只有一个进展可以显示。将进度条的值绑定到 progressvm 的属性。通过 DI 将 progressvm 传递到搜索中。在 search.cs 中设置 value 属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 2018-05-16
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多