长期任务中的进度条进度

时间:2017-11-07 14:40:39

标签: c# wpf xaml mvvm progress-bar

这是我在这里发表的第一篇文章,所以我希望我能做好一切。

我正在使用.NET Framework 4 Client Profile。

我想将.doc文件中的数据加载到我的程序中并使用这些信息。这可能需要花费很多时间,因为我需要浏览文档表并查看其中的内容。这已经有效了,这里唯一的问题是屏幕冻结了,你不能看看是否有事情发生。

另外我知道excel会更快更方便,但由于这种类型的数据总是存储在我们公司的word文档中,所以我必须保持这样。

所以我想要做的是计算我必须读取的表中的所有行,将其设置为Progress-Bar的最大值,然后在每行之后计算值+ 1。

我的加载ButtonCommand绑定到LoadWordDocCmd和进度条:

<Button Name="btnLoadFile" 
        Content="Load" Height="23" 
        Command="{Binding LoadWordDocCmd}"
        HorizontalAlignment="Right" Margin="0,22,129,0" 
        VerticalAlignment="Top" Width="50" 
        Visibility="{Binding VisModeAddNew}"
        />

<ProgressBar HorizontalAlignment="Left" Height="24" Margin="574,52,0,0" 
             VerticalAlignment="Top" Width="306"
             Name="prgBarAddNewLoadWord"
             Minimum="0"
             Maximum="{Binding AddNewProgressBarMaxVal, Mode=OneWay}"
             Value="{Binding AddNewProgressBarValue, Mode=OneWay}"
             Visibility="{Binding AddNewProgressBarVisible}"/>

以下是RelayCommand

/// <summary>
/// Relaycommand for Function loadWordDocument
/// </summary>
public RelayCommand LoadWordDocCmd
{
  get
  {
    if (this.m_loadWordDocCmd == null)
    {
      this.m_loadWordDocCmd = new RelayCommand(this.loadWordDocument, canLoadWordDoc);
    }
    return m_loadWordDocCmd;
  }
  private set
  {
    this.m_loadWordDocCmd = value;
  }
}

/// <summary>
/// checks if the Word Document can be loaded
/// </summary>
/// <param name="parameter">not used</param>
/// <returns>if it could Execute, then true, else false</returns>
private bool canLoadWordDoc(object parameter)
{
  bool ret = false;

  if (this.m_fileSelected)
  {
    ret = true;
  }
  return ret;
}

我已经做过的是使用BackgroundWorker。 我能够将Button-Command绑定到RelayCommandBackgroundWorker的函数,但之后我再也无法检查canExecute函数了。

我用它来测试Progress-Bar,它正在运行:

XAML:

  <Button   ...
            Command="{Binding Path=InstigateWorkCommand}" 
            />

cs:

     private BackgroundWorker worker; 
     private ICommand instigateWorkCommand;

     public ProggressbarSampleViewModel()
     {
        this.instigateWorkCommand = new 
                      RelayCommand(o => this.worker.RunWorkerAsync(), o => !this.worker.IsBusy);
        this.worker = new BackgroundWorker();
        this.worker.DoWork += this.DoWork;
        this.worker.ProgressChanged += this.ProgressChanged;
    }


    public ICommand InstigateWorkCommand
    {
        get { return this.instigateWorkCommand; }
    }

    private int _currentProgress;
    public int CurrentProgress
    {
        get { return this._currentProgress; }
        private set
        {
            if (this._currentProgress != value)
            {
                this._currentProgress = value;
                OnPropertyChanged("CurrentProgress"); 
            }
        }
     }

     private void ProgressChanged(object sender, ProgressChangedEventArgs e)
     {
        this.CurrentProgress = e.ProgressPercentage;
     }

    private void DoWork(object sender, DoWorkEventArgs e)
    {
        // do time-consuming work here, calling ReportProgress as and when you can   
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(1000);
            _currentProgress = i;
            OnPropertyChanged("CurrentProgress");
        }
    }

但是如何让它与canExecute一起使用呢?这是我的函数 - 标题:

/// <summary>
/// Function for Load Word Document
/// </summary>
/// <param name="parameter">not used</param>
private void loadWordDocument(object parameter)

这是Relay-Command类:

public class RelayCommand : ICommand
  {
    private readonly Action<object> methodToExecute;

    private readonly Func<object, bool> canExecute;

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
      EventHandler handler = CanExecuteChanged;
      if (handler != null)
      {
        handler(this, EventArgs.Empty);
      }
    }

    public RelayCommand(Action<object> execute)
      : this(execute, null) { }

    public RelayCommand(Action<object> methodToExecute, Func<object, bool> canExecute)
    {
      this.methodToExecute = methodToExecute;
      this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
      // wird keine canExecute-Funktion übergeben, so liefert diese
      // true zurück, ansonsten wird die custom canExecute-Funktion
      // mit den übergebenen Parametern aufgerufen.
      return canExecute == null ? true : canExecute.Invoke(parameter);
    }

    public void Execute(object parameter)
    {
      methodToExecute(parameter);
    }
  }

感谢您的帮助,我希望我能正确发布这个问题!

2 个答案:

答案 0 :(得分:1)

我希望我能正确理解你的问题。

GUI应用程序的基本规则是:不要使用GUI线程进行(耗时)数据处理。您必须在后台线程上执行此任务。

由于您使用的是.NET 4.0客户端配置文件,因此您无法使用#include <iostream> struct barWay {}; struct noBarWay {}; template <int> struct Bar { }; template <typename> struct selectType { typedef noBarWay type; }; template <int N> struct selectType< Bar<N> > { typedef barWay type; }; template <typename T> struct Foo { void foo (noBarWay const &) { std::cout << "not Bar version" << std::endl; } void foo (barWay const &) { std::cout << "Bar version" << std::endl; } void foo () { foo(typename selectType<T>::type()); } }; int main () { Foo<int> fi; Foo< Bar<42> > fb; fi.foo(); fb.foo(); } / async功能。然而,这将是最简单的解决方案。

您可以使用await来执行此操作。不推荐ThreadPool

在您的XAML中,您将BackgroundWorker属性绑定到ProgressBar.Value属性,因此我假设您已拥有具有该属性的视图模型。您必须确保更改AddNewProgressBarValue会引发AddNewProgressBarValue事件。好消息是,WPF绑定引擎自动将属性值传输操作封送到GUI线程,因此您无需关心哪个线程正在更改您的进度条绑定的属性。

所以解决方案可能看起来像这样(不是生产代码,只是一个想法!):

PropertyChanged

答案 1 :(得分:0)

我认为您的ProggressbarSampleViewModel代码示例没问题。我测试了它并且它有效。

我假设您要更改LoadWordDocCmd以获得InstigateWorkCommand的行为。如果您将ProgressbarSampleViewModel中的代码放入实际的ViewModel中,那么访问loadWordDocumentcanLoadWordDoc应该没有问题。另外,正如mm8所述,在DoWork方法中,您需要调用RaiseCanExecuteChanged,否则WPF将不会检查CanExecute方法。

您的ViewModel应该看起来像下面。请参阅大写的评论。

private BackgroundWorker worker;
private RelayCommand instigateWorkCommand;  //CHANGE HERE

bool isBusy = false;  // ADD THIS
public ProggressbarSampleViewModel()
{
    //CHANGE NEXT LINE
    this.instigateWorkCommand = new RelayCommand(
        o => this.worker.RunWorkerAsync(),
        o => !isBusy && canLoadWordDoc(null));
    this.worker = new BackgroundWorker();
    this.worker.DoWork += this.DoWork;

    //REMOVE
    //this.worker.ProgressChanged += this.ProgressChanged;
}


public ICommand InstigateWorkCommand
{
    get { return this.instigateWorkCommand; }
}

private int _currentProgress;
public int CurrentProgress
{
    get { return this._currentProgress; }
    private set
    {
        if (this._currentProgress != value)
        {
            this._currentProgress = value;
            OnPropertyChanged("CurrentProgress");
        }
    }
}

//REMOVE
//private void ProgressChanged(object sender, ProgressChangedEventArgs e)
//{
//    this.CurrentProgress = e.ProgressPercentage;
//}

private void DoWork(object sender, DoWorkEventArgs e)
{
    //ADD NEXT LINES
    isBusy = true;
    Application.Current.Dispatcher.BeginInvoke(
            (Action)instigateWorkCommand.RaiseCanExecuteChanged);

    // do time-consuming work here, calling ReportProgress as and when you can   
    for (int i = 0; i <= 100; i++)
    {
        Thread.Sleep(10);
        _currentProgress = i;
        OnPropertyChanged("CurrentProgress");
    }

    //ADD NEXT LINES
    isBusy = false;  
    Application.Current.Dispatcher.BeginInvoke(
        (Action)instigateWorkCommand.RaiseCanExecuteChanged);
}

bool m_fileSelected = true;  //CHANGE TO SEE THE EFFECT

//REMOVE
//RelayCommand m_loadWordDocCmd;
///// <summary>
///// Relaycommand for Function loadWordDocument
///// </summary>
//public RelayCommand LoadWordDocCmd
//{
//    get
//    {
//        if (this.m_loadWordDocCmd == null)
//        {
//            this.m_loadWordDocCmd = new RelayCommand(this.loadWordDocument, canLoadWordDoc);
//        }
//        return m_loadWordDocCmd;
//    }
//    private set
//    {
//        this.m_loadWordDocCmd = value;
//    }
//}

/// <summary>
/// checks if the Word Document can be loaded
/// </summary>
/// <param name="parameter">not used</param>
/// <returns>if it could Execute, then true, else false</returns>
private bool canLoadWordDoc(object parameter)
{
    bool ret = false;

    if (this.m_fileSelected)
    {
        ret = true;
    }
    return ret;
}

/// <summary>
/// Function for Load Word Document
/// </summary>
/// <param name="parameter">not used</param>
private void loadWordDocument(object parameter)
{
}

希望这有帮助。