Wpf INotifyPropertyChanged问题

Wpf INotifyPropertyChanged问题,wpf,data-binding,inotifypropertychanged,Wpf,Data Binding,Inotifypropertychanged,首先我想说,下面的示例过于简单化了。 假设您已经绑定了WPF控件 <Window Title="Window1" Height="300" Width="300"> <Grid> <StackPanel> <TextBox Text="{Binding Name}" Margin="10"/> <Button HorizontalAlignment="Center" Content="

首先我想说,下面的示例过于简单化了。 假设您已经绑定了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>
也就是说,每当用户试图从UI更改某个名称时,都会为该名称分配“某个名称”。 但这个示例不起作用。我将TextBox中的名称更改为某个值,按tab键强制焦点移动到按钮,TextBox中的值保持不变,尽管触发了PropertyChanged事件

你能解释一下为什么会这样吗?据我所知,
PropertyChanged
事件强制用户界面从属性中重新读取值并显示它们,但在我的示例中,数据绑定文本框中的值不会更新


再说一遍。我理解这是对属性的糟糕实现,但我想重申,这是过于简单化了。 这只是一个样本。
但无论如何,PropertyChanged表示属性已更改,应该更新,但它没有更改。

原因是您在setter中硬编码了“某个名称”。当您更改文本框值时,setter实际上会被调用,它会再次将“Some Name”设置为propertyValue,以便在UI中看起来不会被更改。
放置
\u name=value
,事情就会像您预期的那样工作,

如果我没有弄错的话,TextBox上文本属性的默认绑定行为是双向的,因此应该可以工作。您可以在XAML中强制它是双向的,如下所示:

<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线程上触发property changed事件,或者对UI线程上的值进行更改

public string MyField  
{  
    get { return _myField; }  
    set {   
        if (_myField == value)  
            return;  

        _myField = value;   

        OnPropertyChanged("MyField");  
    }  
}  
当对象绑定到UI元素时,必须在UI线程上对可能影响UI的对象进行更改

public string MyField  
{  
    get { return _myField; }  
    set {   
        if (_myField == value)  
            return;  

        _myField = value;   

        OnPropertyChanged("MyField");  
    }  
}  
这是该属性的正确实现


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

替换表单中的setter

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

解决了该问题,但仍处于打开状态。为什么我应该进行异步调用,而不是同步发送我的属性已更改的信号。

文本框会忽略PropertyChanged事件,因为它是事件的发起方

一些澄清:

TextBox(或TextBox上的绑定)知道它是启动器,因为它在同一调用中接收PropertyChanged事件。通过执行异步调用,文本框(或绑定)无法知道它是发起方,因此它将处理事件,就像其他人更新了它一样


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

正如Bubblewrap已经指出的那样,这是经过设计的——文本框假设如果将绑定属性设置为某个值,则setter不会更改该值,他们不会更改此行为,因为这会破坏现有代码


如果要更改该值(是的,有充分的理由这样做),则必须使用变通方法,例如,通过添加虚拟转换器。有(不是我写的)详细描述了这项技术。

当绑定的UpdateSourceTrigger被属性更改时,Heinzi(描述的)建议的虚拟转换器解决方案不起作用。但如果这是我们需要的呢

使绑定异步似乎可以达到目的,例如:

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

TextBox(或TextBox上的绑定)知道它是启动器,因为它在同一调用中接收PropertyChanged事件。通过执行异步调用,文本框(或绑定)无法知道它是发起方,因此它将处理事件,就像其他人更新了itErr一样。我可能错了,但它不应该是
{binding Path=Name}
?不。对于属性绑定,您可以省去路径作为快捷方式。这是因为绑定有一个以路径为参数的构造函数。我认为OP实际上希望每次有人更改属性值时,无论键入什么,属性值都是“某个名称”。