2013-02-11 3 views
1

У меня есть ProgressBar и его значение переплетено свойство:Обновление прогрессбара от фонового потока

   <ProgressBar x:Name="progressBar" 
         Margin="0,2,0,0" 
         Height="20" 
         Value="{Binding CompassLogLoadPercent}" 
         Foreground="Blue" 
         Visibility="{Binding CompassLogLoadCompleted, 
       Converter={StaticResource BooleanToVisibilityConverter}}" 
         ToolTip="Loading"> 
      </ProgressBar> 

и свойство:

 public double CompassLogLoadPercent { 
     get { return _compassLogLoadPercent; } 
     private set { 
      if (value != _compassLogLoadPercent) { 
       _compassLogLoadPercent = value; 
       NotifyPropertyChanged(); 
      } 
     } 
    } 

и в отдельной нити его значение обновляется:

for (int j = 0; j < lines.Count(); j++) { 
     ... 
     CompassLogLoadPercent = ((double) j /lines.Count())*100; 
    } 

и поток создается с помощью ЗАДАЧА:

Task.Run(() => { LoadLogFile(fileName); }); 

Почему прогресс не обновляется и как это исправить?

UPDATE: Подробнее

DataContext: (я уверен, что DataContext правилен)

cLT.progressBar.DataContext = logSession; 

и реализация INotifyPropertyChanged

 public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void NotifyPropertyChanged(
     [CallerMemberName] String propertyName = "") { 
     PropertyChangedEventHandler eventHandler = PropertyChanged; 

     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
+2

Вы действительно должны использовать ['Dispatcher'] (http: // msdn .microsoft.com/en-us/magazine/cc163328.aspx) ... –

+1

Вы не указали, где вы установили свой 'DataContext'. –

+0

Проверьте этот ответ: [stackoverflow.com/questions/4621623][1] [1]: http://stackoverflow.com/questions/4621623/wpf-multithreading-ui-dispatcher-in-mvvm – faceman

ответ

4

Проблема лежит где-то в то, что вы нам не показали. Основной метод звучит. (В частности, нет ничего плохого в том, что вы поднимаете PropertyChanged уведомления о событиях рабочего потока, поскольку система привязки данных WPF обнаруживает, когда это происходит, и автоматически устраивает обновление целевого элемента пользовательского интерфейса в потоке пользовательского интерфейса.)

Вот полный пример это действительно работает. Вот ваш XAML:

<Window x:Class="BackgroundThreadUpdate.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"> 
    <Grid> 
     <ProgressBar 
      x:Name="progressBar" 
      VerticalAlignment="Top" Height="20" 
      Value="{Binding CompassLogLoadPercent}"> 
     </ProgressBar> 
     <Button Content="Button" HorizontalAlignment="Left" Margin="10,25,0,0" VerticalAlignment="Top" 
       Width="75" RenderTransformOrigin="-1.24,-0.045" Click="Button_Click_1"/> 

    </Grid> 
</Window> 

и вот ваш отделенного кода:

using System.ComponentModel; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Windows; 

namespace BackgroundThreadUpdate 
{ 
    public partial class MainWindow : Window 
    { 
     private MySource _src; 
     public MainWindow() 
     { 
      InitializeComponent(); 
      _src = new MySource(); 
      DataContext = _src; 
     } 

     private void Button_Click_1(object sender, RoutedEventArgs e) 
     { 
      Task.Run(() => 
      { 
       for (int i = 0; i < 100; ++i) 
       { 
        Thread.Sleep(100); 
        _src.CompassLogLoadPercent = i; 
       } 
      }); 
     } 
    } 

    public class MySource : INotifyPropertyChanged 
    { 
     private double _compassLogLoadPercent; 
     public double CompassLogLoadPercent 
     { 
      get 
      { 
       return _compassLogLoadPercent; 
      } 
      set 
      { 
       if (_compassLogLoadPercent != value) 
       { 
        _compassLogLoadPercent = value; 
        OnPropertyChanged("CompassLogLoadPercent"); 
       } 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
    } 
} 

Это иллюстрирует рабочую версию методики, которую вы пытаетесь использовать.

Так что факт, что ваш не работает, должен быть вызван тем, что вы нам не показали. Возможные пояснения:

  • Возможно, ваш поток пользовательского интерфейса заблокирован. Если потоки пользовательского интерфейса заняты, обновления привязки данных не будут обрабатываться. (Когда привязка данных обнаруживает изменение из источника данных в рабочем потоке, он отправляет сообщение в соответствующий поток диспетчера, и это сообщение не будет обрабатываться, если поток диспетчера занят.)
  • Источник данных может не быть в DataContext для вашего ProgressBar - вы не показали нам, где вы устанавливаете контекст, так что это может быть неправильно.
  • Код, который вызывает событие PropertyChanged (ваш код NotifyPropertyChanged), может быть неправильным - вы не указали этот код, и неясно, как он знает, какое имя свойства использовать при поднятии события.

Чтобы проверить первый, просто проверьте, соответствует ли ваш интерфейс пользовательскому вводу при выполнении этой фоновой работы. Если это не так, то поэтому обновления не проходят.

Обновлено 25 февраля добавить соответствующую ссылку

Размышляя о том, что еще я мог бы сказать о том, как справиться с этим сценарием, я пришел к выводу, что она была слишком большой, чтобы поместиться в один StackOverflow ответ. поэтому я написал серию сообщений в блогах о соображениях производительности при выполнении нетривиальной обработки в фоновом потоке, который должен загружать информацию в пользовательский интерфейс: http://www.interact-sw.co.uk/iangblog/2013/02/14/wpf-async-too-fast

+0

U полностью прав, проблема в том, что поток пользовательского интерфейса занят, любой трюк, чтобы решить эту проблему? – persianLife

+0

, но мой пользовательский интерфейс реагирует, поскольку все это в фоновом потоке, но я добавляю много элементов, используя ObservableCollection. – persianLife

+0

Возможно, ваш фоновый поток генерирует так много работы для потока пользовательского интерфейса, что он полностью замалчивает его. Перемещение работы в фоновый поток имеет смысл, когда эта работа идет медленно. Но похоже, что вы делаете то, что быстро производит много данных? На самом деле вы можете замедлять работу, делая их в отдельном потоке! Из вашего кода, похоже, что вы читаете данные из файла и заполняете пользовательский интерфейс этими данными - это правильно? Сколько времени это займет? –