简体   繁体   English

WPF绑定未从DispatcherTimer更新

[英]WPF Binding not updating from DispatcherTimer

The following is a stripped back version of a problem I'm encountering. 以下是我遇到的问题的简化版本。 It's a fairly common issue, but I'm struggling to find the solution. 这是一个相当普遍的问题,但是我正在努力寻找解决方案。

I've an instantiated class which I've bound to an item on my main window. 我有一个实例化的类,它已绑定到主窗口上的一个项目。 This class contains a DispatcherTimer which is used to update a value. 此类包含一个DispatcherTimer,用于更新值。 In the example given it's incrementing this value by 1 every second. 在给出的示例中,它每秒将此值增加1。

I'd expect the bound item on my form to reflect this change by updating its value accordingly, however it never updates. 我希望表单上的绑定项目通过相应地更新其值来反映此更改,但是它永远不会更新。

From reading other responses to similar questions on StackOverflow I've a feeling this is due to the nature of the main UI thread running separately to the thread which is causing the increment. 通过阅读关于StackOverflow上类似问题的其他答复,我感到这是由于主UI线程与导致该增量的线程分别运行而导致的。

I'm banging my head against a wall though trying to get this binding to update with each call of my DispatcherTimer. 尽管试图使此绑定随着我的DispatcherTimer的每次调用进行更新,但我还是将头撞在墙上。

The following is the form element I'm wanting to update every second: 以下是我想每秒更新的表单元素:

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

Next, this is the instantiation of the class containing the timer and my applications configuration: 接下来,这是包含计时器和我的应用程序配置的类的实例化:

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

Lastly, here's the class I've created. 最后,这是我创建的类。 When created it configures a timer which it uses to update a value every second. 创建后,它将配置一个计时器,该计时器用于每秒更新一次值。 Each time this value is updated I'd expect the main UI to be notified of the change and update accordingly. 每次更新此值时,我都希望通知主用户界面更改并相应地进行更新。 However, this message doesn't seem to be getting through. 但是,此消息似乎没有通过。

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

I think I've managed to avoid the usual pitfalls of forgetting INotifyPropertChanged, and other bound values from other models are working just fine. 我想我已经避免了忘记INotifyPropertChanged的常见陷阱,并且其他模型的其他绑定值也很好用。 It's just this property which is being updated via a thread that I'm having trouble with. 只是这个属性正在通过我遇到问题的线程进行更新。 I've also tried creating a similar timer using a simple Timer but I'm having the same problem. 我也尝试过使用简单的计时器创建类似的计时器,但是我遇到了同样的问题。

Any thoughts would be very much appreciated, thanks! 任何想法将不胜感激,谢谢!

I believe your problem is in the call to OnPropertyChanged : 我相信您的问题出在OnPropertyChanged的电话中:

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

That should be 那应该是

OnPropertyChanged("val");

The string in the call to OnPropertyChanged has to match the name of the property. 调用OnPropertyChanged的字符串必须与属性名称匹配。

EDIT 编辑

The reason you want the name passed to OnPropertyChanged to always match the name of the property is because the data binding subscribes to your object's PropertyChanged event and is watching for the value in that string in the parameter passed to its event handler. 您希望传递给OnPropertyChanged的名称始终与属性名称匹配的原因是,因为数据绑定预订了对象的PropertyChanged事件,并且正在监视传递给其事件处理程序的参数中该字符串中的值。 If the name passed doesn't match the name it is looking for, it ignores the notification. 如果传递的名称与要查找的名称不匹配,它将忽略该通知。 It only updates the value of the control bound to that property when the names match. 仅在名称匹配时才更新绑定到该属性的控件的值。

As Aron mentioned in the comments, you can use the CallerMemberAttribute in your OnPropertyChanged method to ensure the property name is always passed to the method properly. 正如阿隆在评论中提到的,你可以使用CallerMemberAttributeOnPropertyChanged方法,以确保属性名称始终传递给方法正确。 According to the answer to this StackOverflow question , your method would look like this: 根据此StackOverflow问题的答案,您的方法将如下所示:

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

You would then call it with no parameter from the property's setter and the name will always be correct. 然后,您将使用属性的setter中的任何参数来调用它,并且名称将始终正确。

As the answer to the linked question says, this code compiles into IL code that is identical to what is produced if you hard code the string in your call, so this trick will always work and will be just as fast. 就像链接问题的答案所说的那样,该代码将编译为IL代码,该代码与您对调用中的字符串进行硬编码时生成的代码相同,因此此技巧将始终有效并且会很快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM