【问题标题】:WPF Binding not updating from DispatcherTimerWPF 绑定未从 DispatcherTimer 更新
【发布时间】:2015-12-27 02:57:01
【问题描述】:

以下是我遇到的问题的精简版。这是一个相当普遍的问题,但我正在努力寻找解决方案。

我有一个实例化的类,我已经绑定到我的主窗口上的一个项目。此类包含用于更新值的 DispatcherTimer。在给出的示例中,它每秒将该值增加 1。

我希望表单上的绑定项通过相应地更新其值来反映此更改,但它永远不会更新。

通过阅读 StackOverflow 上对类似问题的其他回复,我感觉这是由于主 UI 线程的性质与导致增量的线程分开运行。

虽然我试图让这个绑定随着我的 DispatcherTimer 的每次调用而更新,但我却把头撞到了墙上。

以下是我想要每秒更新的表单元素:

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

接下来,这是包含计时器和我的应用程序配置的类的实例化:

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

最后,这是我创建的类。创建时,它配置一个计时器,用于每秒更新一个值。每次更新此值时,我希望主 UI 会收到更改通知并相应更新。但是,这条消息似乎没有通过。

类 BasicTimer:INotifyPropertyChanged { DispatcherTimer _timer; uint _val = 10; 公共单位价值 { 得到 { 返回_val; } 放 { 如果(_val!=值) { _val = 价值; OnPropertyChanged("值"); } } } 公共基本定时器() { _timer = new DispatcherTimer(); _timer.Tick += new EventHandler(TimerTick); _timer.Interval = new TimeSpan(0, 0, 1); _timer.Start(); } private void TimerTick(对象发送者,EventArgs e) { 验证++; Console.WriteLine(val); } 公共事件 PropertyChangedEventHandler PropertyChanged; 受保护的无效 OnPropertyChanged(字符串 PropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(PropertyName)); } }

我想我已经设法避免了忘记 INotifyPropertChanged 的​​常见陷阱,并且来自其他模型的其他绑定值工作得很好。只是这个属性正在通过我遇到问题的线程进行更新。我也尝试使用简单的 Timer 创建类似的计时器,但我遇到了同样的问题。

任何想法将不胜感激,谢谢!

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    我相信您的问题在于拨打OnPropertyChanged

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

    应该是这样的

    OnPropertyChanged("val");
    

    OnPropertyChanged 调用中的字符串必须与属性名称匹配。

    编辑

    您希望传递给OnPropertyChanged 的名称始终与属性名称匹配的原因是,数据绑定订阅了您的对象的PropertyChanged 事件,并在传递给它的参数中监视该字符串中的值事件处理程序。如果传递的名称与它正在寻找的名称不匹配,它会忽略通知。它仅在名称匹配时更新绑定到该属性的控件的值。

    正如 Aron 在 cmets 中提到的,您可以在您的 OnPropertyChanged 方法中使用 CallerMemberAttribute 来确保始终正确地将属性名称传递给该方法。根据this StackOverflow question的回答,你的方法应该是这样的:

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

    然后,您可以在不使用属性设置器的参数的情况下调用它,并且名称将始终正确。

    正如链接问题的答案所说,此代码编译为 IL 代码,与您在调用中对字符串进行硬编码时生成的代码相同,因此此技巧将始终有效并且同样快。

    【讨论】:

    • 另外,他不应该从 dispatched 方法中更新属性吗?在 WPF 中,除了 UIThread 之外,不能从其他地方进行任何 UI 交互
    • WPF 对INotifyPropertyChanged 接口的支持允许它处理对源自另一个线程的对象的更新。虽然我对细节不是很熟悉,但我相信 WPF 会收到通知并在 UI 线程的 Dispatcher 上安排代码来处理事件。由于 OP 的类有一个DispatchTimer,但是,如果对象在 UI 线程上实例化,那么 DispatcherTimer 无论如何都会在 UI 线程上运行。
    • @TonyVitabile 我建议添加一些关于使用CallerMemberNameAttribute 的内容,以及为什么您希望OnPropertyChanged 调用与属性名称完全匹配。
    • @TonyVitabile 已检查,您是正确的。将 OnPropertyChanged 的​​参数设置为“val”,一切顺利。感谢您的宝贵时间!
    猜你喜欢
    • 1970-01-01
    • 2011-03-06
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 2022-12-04
    • 2013-06-01
    • 2014-04-28
    • 2012-06-18
    相关资源
    最近更新 更多