【问题标题】:Update progressbar from thread- MVVM从线程更新进度条 - MVVM
【发布时间】:2015-07-30 22:49:21
【问题描述】:

我是 MVVM 模式的新手。我从另一个线程更新进度条时遇到问题。

View.Xaml

<ProgressBar Value="{Binding ProgressBarValue}">

ViewModel.cs

public int ProgressBarValue 
{
            get
            {
                return status;
            }
            set
            {
                this.status = value;
                this.RaisePropertyChanged(() => this.ProgressBarValue);
            }
}


Thread th;

private void OnOk()
{
   th=new Thread(new ThreadStart(StartProcess));
}

private void StartProcess()
{
  utilityclass.DoWork();
}

UtilityClass.cs

public void DoWork()
{
   for(i=0;i<100;i++)
   {
      //dosomething
   }
}

我的问题是如何将 DoWork 方法的进度更新到视图?我通过使用后台工作类找到了许多解决方案。但由于某些原因,我无法遵循这些方法。我真的需要帮助。

【问题讨论】:

  • 您可以使用Dispatcher 向MainUI Thread 发送信息。
  • @NikhilAgrawal 你有任何例子吗?如果有,请分享。
  • “但由于某些原因,我无法遵循这些方法。” - 这些原因是什么,您尝试了哪些方法?

标签: c# wpf multithreading mvvm thread-safety


【解决方案1】:

我只需将ProgressChanged 事件添加到UtilityClass

class UtilityClass
{
    public event EventHandler<ProgressChangedEventHandler> ProgressChanged;

    public class ProgressChangedEventHandler : EventArgs
    {
        public int Progress
        {
            get;
            private set;
        }

        public ProgressChangedEventHandler(int progress)
        {
            Progress = progress;
        }
    }

    public void DoWork()
    {
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(300);

            if (ProgressChanged != null)
                ProgressChanged(this, new ProgressChangedEventHandler(i));
        }
    }
}

然后您可以在 View Model 中订阅此事件,并在每次引发事件时更新属性值。

    public ShellViewModel()
    {
        utilityclass = new UtilityClass();
        utilityclass.ProgressChanged += utilityclass_ProgressChanged;
    }

    void utilityclass_ProgressChanged(object sender, UtilityClass.ProgressChangedEventHandler e)
    {
        ProgressBarValue = e.Progress;
    }

哦,请注意,您实际上应该启动线程!而且我认为这种方法可能需要公开。

    public void OnOk()
    {
        th = new Thread(new ThreadStart(StartProcess));
        th.Start();
    }

另外,您可能希望在用户关闭表单时中止此线程。

此代码将取决于您的 MVVM 框架。这里我使用的是从 Caliburn.Micro 的 Screen 类派生的 View Model,所以我可以覆盖 OnDeactivate 方法并在那里关闭线程。

    protected override void OnDeactivate(bool close)
    {
        if (close)
        {
            th.Abort();
        }
    }

【讨论】:

    【解决方案2】:

    按照 cmets 的建议,您应该使用 Dispatcher 来更新 ui-bound 值。

    在您的方法 DoWork 中,您应该调用 UI 组件的适当 Dispatcher 来调用 UI 线程中的更新过程。由于您使用 MVVM,您的 ViewModel 不应该知道任何有关 UI 组件的信息,因此您可以使用

    Application.Current.Dispatcher.Invoke( () => { ProgressBarValue = i; } );
    

    这将更新您的进度条。

    另外,看看更常见的 INotifyPropertyChanged 实现,比如 ReSharper 中的一个,它带有值检查是否真的被改变了。

    【讨论】:

    • 我的 Utilityclass 无法访问 ProgressBarvalue 属性,因为它是另一个类。那么我该如何使用上述方法。
    • 一开始你打算如何更新进度条?您可能希望使属性可访问,您可能希望将一些更新回调传递给 DoWork。有很多方法可以做到这一点,包括您的工作人员触发事件,以及您的虚拟机订阅它们,这取决于您。网上有很多例子,比如this
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-22
    相关资源
    最近更新 更多