从thread-MVVM更新进度条

时间:2015-05-19 08:07:01

标签: c# wpf multithreading mvvm thread-safety

我是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方法的进度到视图?我通过使用后台工作者类找到了许多解决方案但由于某些原因,我无法遵循这些方法。我真的需要帮助。

2 个答案:

答案 0 :(得分:1)

正如评论中所建议的那样,您应该使用Dispatcher来更新ui-bound值。

在您的方法DoWork中,您应该调用适当的Dispatcher Ui组件来调用UI线程中的更新过程。由于您使用的是MVVM,因此您的ViewModel不应该对UI组件有任何了解,因此您可以使用

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

这将更新您的progressBar。

另外,看看更常见的INotifyPropertyChanged实现,例如来自ReSharper的实现,并进行值检查以实际更改。

答案 1 :(得分: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();
        }
    }