【问题标题】:INotifyPropertyChanged problemINotifyPropertyChanged 问题
【发布时间】:2010-10-03 14:29:57
【问题描述】:

首先我想说下面的示例过于简单化了。 假设您已绑定 WPF 控件。

<Window Title="Window1" Height="300" Width="300">
<Grid>
    <StackPanel>
        <TextBox Text="{Binding Name}" Margin="10"/>
        <Button HorizontalAlignment="Center" 
        Content="Click Me" Margin="5" 
        Padding="2" Click="OnButtonClick" />
    </StackPanel>
</Grid>
</Window>

Window 绑定到实现INotifyPropertyChanged 的 Person 类,并在表单中有 Name 设置器

    public string Name 
    {
        get { return _name; }
        set 
        {
            _name = "Some Name";
            OnPropertyChanged("Name");
        }
    }

即每当用户尝试从 UI 更改它时,_name 都会被分配“某个名称”。 但是这个示例不起作用。我将 TextBox 中的名称更改为某个值,按下制表符强制焦点移动到 Button,尽管触发了 PropertyChanged 事件,但 TextBox 中的值保持不变。

你能解释一下为什么会这样吗?据我了解PropertyChanged 事件强制 UI 从属性中重新读取值并显示它们,但在我的示例中,数据绑定文本框中的值没有更新。


再次。我知道这是该属性的不良实现,但我想重申这是过于简单化了。 这只是一个样本。 但无论如何,PropertyChanged 表示属性已更改并且应该更新但它没有。

【问题讨论】:

  • 呃,我可能错了,但不应该是{Binding Path=Name}吗?
  • 不。对于属性绑定,您可以省略 Path 作为快捷方式。
  • 这是因为 Binding 有一个以路径为参数的构造函数。

标签: wpf data-binding inotifypropertychanged


【解决方案1】:

TextBox 忽略 PropertyChanged 事件,因为它是事件的发起者。

一些澄清:

TextBox(或文本框上的绑定)知道它是发起者,因为它在同一个调用中接收到 PropertyChanged 事件。通过进行异步调用,文本框(或绑定)无法知道它是发起者,因此它会像其他人更新它一样处理事件

如果您将第二个文本框添加到您的 UI,您会看到第二个文本框在您编辑第一个文本框时会发生变化,反之亦然。

【讨论】:

  • 文本框(或文本框上的绑定)知道它是发起者,因为它在同一个调用中接收到 PropertyChanged 事件。通过进行异步调用,文本框(或绑定)无法知道它是发起者,因此它会像其他人更新它一样处理事件
【解决方案2】:

当绑定的 UpdateSourceTriggerPropertyChanged 时,Heinzi 建议的虚拟转换器解决方法(描述为 here)不起作用。但如果这是我们需要的呢?

似乎使绑定异步可以解决问题,例如:

SelectedIndex="{Binding SelectedIndex, IsAsync=True}"

【讨论】:

    【解决方案3】:

    正如 Bubblewrap 已经指出的那样,这是设计使然——文本框假定如果将绑定属性设置为某个值,则设置器不会更改该值。 According to Microsoft,他们不会改变这种行为,因为这会破坏现有的代码。

    如果您想要更改该值(是的,这样做有充分的理由),您必须使用一种解决方法,例如,通过添加一个虚拟转换器。有a blog entry(不是我写的)详细描述了这种技术。

    【讨论】:

      【解决方案4】:

      原因是您在 setter 中硬编码了“Some Name”。当您更改 textBox 值时,实际上调用了 setter 并再次将“Some Name”设置为 propertyValue,因此它似乎没有在 UI 中更改。 输入_name = value,一切都会如您所愿,

      【讨论】:

      • 我认为 OP 实际上希望每次有人更改值时属性值都是“Some Name”,无论他们输入什么。
      【解决方案5】:
      public string MyField  
      {  
          get { return _myField; }  
          set {   
              if (_myField == value)  
                  return;  
      
              _myField = value;   
      
              OnPropertyChanged("MyField");  
          }  
      }  
      

      这是该属性的正确实现。

      更改属性时,请确保对象的完全相同的实例绑定到控件。否则,将通知更改,但控件永远不会得到它,因为控件没有正确绑定。

      【讨论】:

        【解决方案6】:

        替换表单中的setter

                set 
                {
                    _name = "Some Name";
                    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, 
                        (SendOrPostCallback)delegate { OnPropertyChanged("Name"); },
                        null);
                }
        

        解决了这个问题,但它仍然是开放的。为什么我应该进行异步调用而不是同步发出我的属性已更改的信号。

        【讨论】:

          【解决方案7】:

          如果我没记错的话,TextBox 上 Text 属性的默认绑定行为是 TwoWay,所以这应该可以。您可以像这样在 XAML 中强制它为 TwoWay:

          <Window Title="Window1" Height="300" Width="300">
            <Grid>
              <StackPanel>
                  <TextBox Text="{Binding Name, Mode=TwoWay}" Margin="10"/>
                  <Button HorizontalAlignment="Center" 
                  Content="Click Me" Margin="5" 
                  Padding="2" Click="OnButtonClick" />
              </StackPanel>
            </Grid>
          </Window>
          

          注意绑定声明中的Mode=TwoWay

          如果这不起作用,那么我怀疑在触发事件或分配属性的代码中引发了异常,您应该寻找它。


          您似乎有可能正在调用以更改不是 UI 线程的线程上的值。如果是这种情况,那么您要么必须编组调用以在 UI 线程上触发属性更改事件,要么对 UI 线程上的值进行更改。

          当对象绑定到 UI 元素时,必须在 UI 线程上对可能影响 UI 的对象进行更改。

          【讨论】:

            猜你喜欢
            • 2013-04-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-07
            相关资源
            最近更新 更多