2015-09-29 6 views
0

Ниже приведена лишенная назад проблема с проблемой, с которой я сталкиваюсь. Это довольно распространенная проблема, но я изо всех сил пытаюсь найти решение.WPF Связывание не обновляется с DispatcherTimer

У меня есть экземпляр класса, который я привязал к элементу в моем главном окне. Этот класс содержит DispatcherTimer, который используется для обновления значения. В приведенном примере он увеличивает это значение на 1 каждую секунду.

Я бы ожидал, что связанный элемент в моей форме отразит это изменение, обновив его значение соответствующим образом, однако оно никогда не обновляется.

От чтения других ответов на подобные вопросы в StackOverflow У меня такое ощущение, что это связано с природой основного потока пользовательского интерфейса, выполняемого отдельно для потока, вызывающего приращение.

Я ударяю головой о стену, пытаясь получить эту привязку для обновления при каждом вызове моего DispatcherTimer.

Ниже приведен элемент формы, я хотел, чтобы обновить каждую секунду:

<TextBox Text="{Binding val}" Width="100"/> 

Далее, это экземпляр класса, содержащего таймер и моя конфигурация приложения:

BasicTimer basictimer; 
public MainWindow() 
{ 
    InitializeComponent(); 
    basictimer = new BasicTimer(); 
    DataContext = basictimer; 
} 

Наконец, вот класс, который я создал. При создании он настраивает таймер, который он использует для обновления значения каждую секунду. Каждый раз, когда это значение обновляется, я ожидаю, что основной пользовательский интерфейс будет уведомлен об изменениях и обновлении соответственно. Однако это сообщение, похоже, не проходит.

 
    class BasicTimer: INotifyPropertyChanged 
    { 
    DispatcherTimer _timer; 

    uint _val = 10; 
    public uint val 
    { 
     get 
     { 
     return _val; 
     } 
     set 
     { 
     if(_val!=value) 
     { 
      _val = value; 
      OnPropertyChanged("Value"); 
     } 
     } 
    } 

    public BasicTimer() 
    { 
     _timer = new DispatcherTimer(); 
     _timer.Tick += new EventHandler(TimerTick); 
     _timer.Interval = new TimeSpan(0, 0, 1); 
     _timer.Start(); 
    } 


    private void TimerTick(object sender, EventArgs e) 
    { 
     val++; 
     Console.WriteLine(val); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

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

Я думаю, что мне удалось избежать обычных ловушек забывания INotifyPropertChanged и другие связанные значений из других моделей работает нормально. Это просто свойство, которое обновляется через поток, с которым у меня возникают проблемы. Я также попытался создать аналогичный таймер, используя простой таймер, но у меня такая же проблема.

Любые мысли были бы очень благодарны, спасибо!

ответ

2

Я считаю, что ваша проблема в вызове OnPropertyChanged:

uint _val = 10; 
public uint val 
{ 
    get 
    { 
    return _val; 
    } 
    set 
    { 
    if(_val!=value) 
    { 
     _val = value; 
     OnPropertyChanged("Value"); 
    } 
    } 
} 

Это должно быть

OnPropertyChanged("val"); 

Строка в вызове OnPropertyChanged должен совпадать с именем свойства.

EDIT

Причина вы хотите имя, переданное OnPropertyChanged всегда совпадает с именем свойства потому, что привязка данных при подписке PropertyChanged события вашего объекта и наблюдает за значением в этой строке в параметр передан его обработчику событий. Если переданное имя не совпадает с именем, которое оно ищет, оно игнорирует уведомление. Он только обновляет значение элемента управления, связанного с этим свойством, когда имена совпадают.

Как указано в комментариях к Aron, вы можете использовать CallerMemberAttribute в своем методе OnPropertyChanged, чтобы гарантировать, что имя свойства всегда передается методу должным образом.Согласно ответу на this StackOverflow question, ваш метод будет выглядеть следующим образом:

protected void OnPropertyChanged([CallerMemberName] string PropertyName = null) 
{ 
    if (PropertyChanged != null) 
    { 
    PropertyChanged(this, new PropertyChangedEventArgs(PropertyName)); 
    } 
} 

Вы бы затем вызвать его без параметров от сеттера отеля и имя всегда будет правильным.

Как ответ на связанный вопрос говорит, что этот код компилируется в код IL, который идентичен тому, что производится, если вы жестко кодируете строку в своем вызове, поэтому этот трюк всегда будет работать и будет таким же быстрым.

+0

Кроме того, разве он не должен обновлять свойство из отправленного метода? В WPF нельзя взаимодействовать с другими локальными пользовательскими интерфейсами, кроме UIThread – SOReader

+0

Поддержка WPF интерфейса 'INotifyPropertyChanged' позволяет обрабатывать обновления объектов, которые происходят из другого потока. Хотя я не знаком с деталями, я считаю, что WPF получает уведомление и расписывает код в «Диспетчере» потока пользовательского интерфейса для обработки события. Однако, поскольку класс OP имеет 'DispatchTimer', однако, если объект создается в потоке пользовательского интерфейса, то, во всяком случае,' DispatcherTimer' будет работать в потоке пользовательского интерфейса. –

+0

@TonyVitabile Я бы рекомендовал добавить немного об использовании 'CallerMemberNameAttribute', а также почему вы хотите, чтобы вызов' OnPropertyChanged' точно соответствовал имени свойства. – Aron