由于某些特殊原因,我在 C# 代码中声明了一个绑定,而不是像往常一样在 XAML 中声明它。我面临的问题是,当我仅更改对象的一个属性时,INotifyPropertyChanged 可以正常工作并按预期传播更改,但是当我更改整个对象(引用)时,它无法按预期工作。
您可以在这个示例代码中看到它,其中我设置了一个初始值“111”,效果很好,然后我只将一个属性值更改为“222”并且也按预期工作,但是当我设置一个新对象时,那么它就不起作用了。
我应该在代码中更改什么,以便第三次分配也起作用并且通过绑定将 Text 属性设置为“333”?
public class MainWindowViewModel : INotifyPropertyChanged
{
private MySubClass _selectedItem;
public MySubClass SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItem));
}
}
private MySubClass _selectedItemBefore;
public MySubClass SelectedItemBefore
{
get => _selectedItemBefore;
set
{
_selectedItemBefore = value;
OnPropertyChanged(nameof(SelectedItemBefore));
}
}
private MySubClass _selectedItemAfter;
public MySubClass SelectedItemAfter
{
get => _selectedItemAfter;
set
{
_selectedItemAfter = value;
OnPropertyChanged(nameof(SelectedItemAfter));
}
}
TextBox textBox = new TextBox();
public MainWindowViewModel()
{
SelectedItemBefore = new MySubClass
{
FirstProperty = "111"
};
SelectedItemAfter = new MySubClass
{
FirstProperty = "333"
};
SelectedItem = SelectedItemBefore;
var firstPropertyBinding = new Binding("FirstProperty") { Source = SelectedItem, Mode = BindingMode.TwoWay };
BindingOperations.SetBinding(textBox, TextBox.TextProperty, firstPropertyBinding);
Console.WriteLine($"Text here is: {textBox.Text}");
// It's "111", so OK!!!
SelectedItem.FirstProperty = "222";
Console.WriteLine($"Text here is: {textBox.Text}");
// It's "222", so OK!!!
SelectedItem = SelectedItemAfter;
Console.WriteLine($"Text here is: {textBox.Text}");
// ¡¡¡FAIL!!!: expected "333" but it is "222" yet
// (here SelectedItem.FirstProperty == "333")
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class MySubClass : INotifyPropertyChanged
{
private string _firstProperty;
public string FirstProperty
{
get => _firstProperty;
set
{
_firstProperty = value;
OnPropertyChanged(nameof(FirstProperty));
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
更新: 我知道 TextBox 永远不应该出现在 ViewModel 中,也不应该出现与 View 相关的任何内容,请不要过多关注这个细节。该代码仅用于学习目的并了解绑定如何工作,但目前我还没有完全理解它。我希望了解如何修改代码以使其显示“333”。
首先我们来看看
Binding
的数据来源:
Source
:直接静态绑定源引用,绑定后不可修改。
{Reference name}
在 XAML 中进行设置,以在此 XAML 范围内设置命名元素引用。RelativeSource
:根据应用绑定的元素和该RelativeSource的模式,根据应用绑定的元素的相对位置获取其他对象作为绑定源。
RelativeSource
目标改变将会改变这个绑定源的引用。ElementName
:设置在当前 XAML 和同一可视化树中定义的使用 x:Name
命名元素的名称。
Popup
控件内部和外部)DataContext
改变将会改变这个绑定源的引用。Setted
Binding.Source
属性将设置对象的静态 引用作为此 Binding
上的绑定源,但看起来你想要一个动态源,所以你不应该使用 Binding.Source
。