2017-11-07 14 views
1

Es ist mein erster Beitrag hier, also hoffe ich, dass ich alles richtig mache.Progressbar Fortschritt während langer Aufgabe

Ich verwende das .NET Framework 4-Clientprofil.

Ich möchte Daten aus einer DOC-Datei in mein Programm laden und mit diesen Informationen arbeiten. Dies kann viel Zeit in Anspruch nehmen, da ich die Tabellen des Dokuments durchgehen und prüfen muss, was sich darin befindet. Das funktioniert schon, das einzige Problem hier ist, dass der Bildschirm friert und man nicht sehen kann, ob etwas passiert.

Auch ich weiß, das wäre schneller und viel einfacher in Excel, aber da diese Art von Daten immer und in Word-Dokumente in unserer Firma gespeichert ist, muss ich es so halten.

Also, was ich tun möchte, ist es, alle Zeilen aus den Tabellen zählen, die ich gelesen habe, setzen Sie diese als mein Maximalwert für die Progress-Bar und dann nach jeder Zeile würde ich den Wert + 1

zählt

ich meine Last haben Button mit dem Command gebunden LoadWordDocCmd und dem Fortschrittsbalken:

<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}"/> 

Hier ist die 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; 
} 

Was ich schon getan habe, war mit einer BackgroundWorker zu arbeiten. Ich konnte den Button-Befehl an eine Funktion binden, die eine RelayCommand mit der BackgroundWorker hat, aber dann konnte ich die canExecute-Funktion nicht mehr überprüfen.

benutzte ich diese die Progress-Bar zu testen, die Arbeit wurde:

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"); 
     } 
    } 

Aber wie kann ich dies mit dem arbeiten kannausführen? Hier ist meine Funktion-Rubrik:

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

Hier ist die Relay-Command-Klasse:

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); 
    } 
    } 

Vielen Dank für Ihre Hilfe und ich hoffe, dass ich gepostet diese Frage richtig!

+0

Verwenden Sie Task und IProgress anstelle von BackgroundWorker eine Option für Sie? Siehe https://stackoverflow.com/a/35316334/982149 – Fildor

+0

Sie sollten die RaiseCanExecuteChanged() - Methode des Befehls immer dann aufrufen, wenn Sie den Status der Schaltfläche aktualisieren möchten. Was funktioniert und was ist nicht ...? – mm8

+0

Ich konnte nicht einmal den RelayCommand mit dem canExecuteChange arbeiten lassen. Bei Verwendung von RelayCommand (o => this.worker.RunWorkerAsync(), o =>! This.worker.IsBusy); Ich wusste nicht, wie man den Funktionszeiger für canLoadDoc zum Arbeiten bringt – kpischi

Antwort

1

Ich hoffe, ich verstehe Ihr Problem richtig.

Die Grundregel für eine GUI-Anwendung lautet: Verwenden Sie den GUI-Thread nicht für die (zeitraubende) Datenverarbeitung. Sie müssen diese Aufgabe in einem Hintergrundthread ausführen.

Da Sie das .NET 4.0-Clientprofil verwenden, steht Ihnen die Funktion async/await nicht zur Verfügung. Das wäre jedoch die einfachste Lösung.

Sie können dies stattdessen mit einem ThreadPool tun. Die BackgroundWorker wird nicht mehr empfohlen.

In Ihrem XAML binden Sie die ProgressBar.Value-Eigenschaft an eine AddNewProgressBarValue-Eigenschaft, also nehme ich an, dass Sie bereits ein Ansichtsmodell mit dieser Eigenschaft haben. Sie müssen sicherstellen, dass das Ändern AddNewProgressBarValue das Ereignis PropertyChanged auslösen wird. Und die gute Nachricht ist, dass die WPF-Binding-Engine die Übertragung der Eigenschaftswerte automatisch zum GUI-Thread marsiert, so dass Sie sich nicht darum kümmern müssen, welcher Thread eine Eigenschaft ändert, an die Ihre Fortschrittsleiste gebunden ist.

So könnte die Lösung so aussieht (nicht ein Produktionscode, nur eine Idee!):

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; 
    } 
} 
+0

hallo, vielen dank das half mein problem zu lösen! Genau das habe ich versucht. Jetzt denke ich, ich muss einige weitere Funktionen ändern, um so zu arbeiten, weil ich gerade jetzt alles auf dem GUI-Thread mache:/Einzige Sache, die ich ändern musste, war: Application.Current.Dispatcher.Invoke (new Action (() => this.LoadWordDocCmd.RaiseCanExecuteChanged())); – kpischi

0

Ich denke, dass Ihr ProggressbarSampleViewModel Codebeispiel in Ordnung ist. Ich habe es getestet und es funktioniert.

Ich gehe davon aus, dass Sie LoadWordDocCmd ändern möchten, um das Verhalten von InstigateWorkCommand zu haben. Wenn Sie den Code von ProgressbarSampleViewModel in Ihr tatsächliches ViewModel einfügen, sollten Sie problemlos auf loadWordDocument und canLoadWordDoc zugreifen können. Darüber hinaus, wie mm8 erwähnt, in Ihrer DoWork Methode müssen Sie RaiseCanExecuteChanged aufrufen oder WPF wird nicht die CanExecute Methode überprüfen.

Ihr ViewModel sollte wie folgt aussehen. Siehe Kommentare in Großbuchstaben.

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) 
{ 
} 

Hoffe das hilft.

Verwandte Themen