2010-10-27 13 views
3

Je souhaite lier la propriété value d'un ProgressBar WPF à une propriété de dépendance mise à jour pendant un processus de longue durée. Si le processus en cours d'exécution est appelé à partir du thread principal, cela bloque l'interface utilisateur (et donc le ProgressBar) de la mise à jour jusqu'à ce que le processus se termine, empêchant la progression désirée à travers le processus affiché. Le processus de longue durée ne peut pas non plus être exécuté en faisant tourner un thread séparé car il n'est pas possible de mettre à jour une propriété de dépendance d'un thread différent à son propriétaire (c'est-à-dire le thread sur lequel il a été créé).Mise à jour de WPF ProgressBar liée à un objet de dépendance exécutant un long processus

Dans le code ci-dessous, lorsque le bouton est cliqué, le processus de longue durée s'exécute et la barre de progression saute de 0% à 100% lorsqu'elle se termine. Au lieu de cela, je veux pouvoir cliquer sur le bouton et faire en sorte que la barre de progression montre la progression à travers la progression en cours (non seulement mise à jour de 0% à 100% lorsque le processus se termine mais montre une progression régulière).

MainWindow.xaml

<Window x:Class="ProgressBarTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <StackPanel> 
     <Button Width="200" Height="50" x:Name="btnRun" Click="btnRun_Click">Run Process</Button> 
     <ProgressBar Width="200" Height="20" x:Name="pbProgress" Minimum="0" Maximum="100" Value="{Binding Path=MyFoo.ProgressValue}"/> 
    </StackPanel> 
</Window> 

MainWindow.xaml.cs

using System.Windows; 
using System.Threading; 

namespace ProgressBarTest 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public Foo MyFoo { get; set; } 

     public MainWindow() 
     { 
      MyFoo = new Foo(); 
      InitializeComponent(); 

     } 

     private void btnRun_Click(object sender, RoutedEventArgs e) 
     { 
      btnRun.IsEnabled = false; 

      MyFoo.LongRunningProcess(); // Since this runs on same thread as UI, progress bar does not update until the long running process completes. 

      btnRun.IsEnabled = true; 
     } 
    } 

    public class Foo : DependencyObject 
    { 
     public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register("ProgressValue", typeof(double), typeof(Foo)); 
     public double ProgressValue 
     { 
      get { return (double)GetValue(ProgressValueProperty); } 
      set 
      { 
       SetValue(ProgressValueProperty, value); 
      } 
     } 

     public Foo() 
     { 
      ProgressValue = 0; 
     } 

     public void LongRunningProcess() 
     { 
      do 
      { 
       ProgressValue += 1; 

       Thread.Sleep(30); 
      } 
      while (ProgressValue < 100); 
     } 
    } 
} 

post-scriptum Je sais que je peux faire cela en passant l'instance de ProgressBar comme argument au processus de longue durée afin qu'elle puisse le mettre à jour directement via Dispatcher.Invoke, mais ce n'est pas ce que je veux. Je souhaite que la barre de progression soit mise à jour via la liaison à une propriété de dépendance.

Merci

Sean

Répondre

2

Je vais essayer d'être plus agréable que Euphoric.

Vous devez exécuter votre LongRunningProcess en utilisant un BackgroundWorker qui exécute le processus sur un thread différent, puis mettre à jour la propriété en utilisant l'événement ProgressChanged

// This event handler updates the progress bar. 
private void backgroundWorker1_ProgressChanged(object sender, 
    ProgressChangedEventArgs e) 
{ 
    MyFoo.ProgressValue = e.ProgressPercentage; 
} 
+0

Merci Eduardo. Cependant, je ne suis pas sûr de savoir comment cela fonctionnerait pour résoudre mon problème. À la suite de l'extrait ci-dessus: L'instance MyFoo doit déclencher l'événement ci-dessus (ProgressChanged) sur le travailleur d'arrière-plan, puis l'événement met à jour la valeur de la propriété MyFoo.ProgressValue? L'instance MyFoo peut bien sûr mettre à jour sa propre propriété (comme dans le code que j'ai montré), le problème est que le ProgressBar de l'interface utilisateur ne se met pas à jour. – sean

2

L'interface utilisateur doit être mis à jour à partir du thread principal. Par conséquent, vous devez utiliser Dispather pour mettre à jour la propriété.

Utilisez ce à partir du LongRunningProcess() pour mettre l'appel sur la file d'attente Dispatcher:

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action<double>(UpdateData), value); 

et la mise en œuvre de la méthode de mise à jour comme ceci:

private void UpdateData(double value){ 
    MyFoo.ProgressValue = value; 
} 

Hope this helps.

1

Je ne sais pas si vous avez trouvé une solution à votre problème, mais c'est comme ça que j'ai résolu un problème simulé dans le passé. Cela vous permet de vous lier à votre DP et de simplement mettre à jour la propriété normale

public static readonly DependencyProperty progressProperty = DependencyProperty.Register("progress", typeof(int), typeof(this)); 
public int progress 
    { 
     get 
     { 
      return (int)this.Dispatcher.Invoke(
        System.Windows.Threading.DispatcherPriority.Background, 
        (DispatcherOperationCallback)delegate { return GetValue(progressProperty); }, 
        progressProperty); 
     } 
     protected set 
     { 
      this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate { SetValue(progressProperty , value); }, value); 
     } 
    }