c# - 长任务期间的进度条进度

标签 c# wpf xaml mvvm progress-bar

这是我在这里的第一篇文章,所以我希望我做的一切都是正确的。

我正在使用 .NET Framework 4 客户端配置文件。

我想将 .doc 文件中的数据加载到我的程序中并使用这些信息。这可能需要很多时间,因为我需要浏览文档的表格并检查里面的内容。这已经奏效了,这里唯一的问题是屏幕卡住,您看不到是否发生了什么事。

我也知道这在 excel 中会更快更容易,但是由于这种类型的数据一直并且一直存储在我们公司的 word 文档中,所以我必须保持这种状态。

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

我有我的负载ButtonCommand绑定(bind)到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 绑定(bind)到具有 RelayCommand 的函数与 BackgroundWorker ,但后来我无法再检查 canExecute 函数了。

我用它来测试进度条,它正在工作:

xml:
  <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)

这是中继命令类:
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);
    }
  }

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

最佳答案

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

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

由于您使用的是 .NET 4.0 客户端配置文件,async/await您无法使用该功能。然而,这将是最简单的解决方案。

您可以使用 ThreadPool反而。 BackgroundWorker不再推荐。

在您的 XAML 中,您将绑定(bind) ProgressBar.Value属性到 AddNewProgressBarValue属性,所以我假设您已经有一个具有该属性的 View 模型。您必须确保更改 AddNewProgressBarValue将提高 PropertyChanged事件。好消息是,WPF 绑定(bind)引擎自动将属性值传输操作编码到 GUI 线程,因此您无需关心哪个线程正在更改您的进度条绑定(bind)到的属性。

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

class ViewModel : INotifyPropertyChanged
{
    private bool isProcessing;
    public bool AddNewProgressBarVisible
    {
        get { return this.isProcessing; }
        // SetProperty here is a PRISM-like helper to set the backing field value
        // and to raise the PropertyChanged event when needed.
        // You might be using something similar.
        private set { this.SetProperty(ref this.isProcessing, value, "AddNewProgressBarVisible");
    }

    private int progressValue;
    public int AddNewProgressBarValue
    {
        get { return this.progressValue; }
        private set { this.SetProperty(ref this.progressValue, value, "AddNewProgressBarValue");
    }

    // This is your command handler
    private void LoadWordDocument(object parameter)
    {
        if (this.isProcessing)
        {
            // don't allow multiple operations at the same time
            return;
        }

        // indicate that we're staring an operation:
        // AddNewProgressBarVisible will set isProcessing = true
        this.AddNewProgressBarVisible = true;
        this.AddNewProgressBarValue = 0;

        // Notify the bound button, that it has to re-evaluate its state.
        // Effectively, this disables the button.
        this.LoadWordDocCmd.RaiseCanExecuteChanged();

        // Run the processing on a background thread.
        ThreadPool.QueueUserWorkItem(this.DoLoadWordDocument);
    }

    private void DoLoadWordDocument(object state)
    {
        // Do your document loading here,
        // this method will run on a background thread.
        // ...

        // You can update the progress bar value directly:
        this.AddNewProgressBarValue = 42; // ...estimate the value first

        // When you're done, don't forget to enable the button.
        this.AddNewProgressBarVisible = false;

        // We have to marshal this to the GUI thread since your ICommand
        // implementation doesn't do this automatically
        Application.Current.Dispatcher.Invoke(() => this.LoadWordDocCmd.RaiseCanExecuteChanged());
    }

    // this is your command enabler method
    private bool CanLoadWordDoc(object parameter)
    {
        // if we're already loading a document, the command should be disabled
        return this.m_fileSelected && !this.isProcessing;
    }
}

关于c# - 长任务期间的进度条进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47160619/

相关文章:

c# - 为什么 Azure Functions 运行时找不到我的 DLL?

c# - 中断后如何连续滚动到scrollviewer MVVM的底部

wpf - 如何在 xaml 中复制 WPF 资源?

c# - 通过 MVVM 将 View 输出到另一个到另一个

c# - 将 PDF 转换为任何可以用 Word 打开的文件

c# - 单击 NavMenu 时 Blazor 不会更新

c# - GridView列宽调整

wpf - FlowDocument 中的波浪下划线

c# - 形状与 DrawingVisual 的性能

windows - 导航页面时 xaml 中的 InvalidCastException