我正在尝试将依赖属性绑定到INotifyPropertyChanged
具有多级属性路径的启用属性。
当属性的所有者对象不是null
时,绑定起作用,但如果所有者对象是null
,则绑定不做任何事情(不调用转换器,TargetNullValue
也不使用转换器)。
这是一些重现问题的最小示例代码:
Window1.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
namespace NPCPropertyPath
{
public abstract class VMBase : INotifyPropertyChanged
{
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (e == null) {
throw new ArgumentNullException("e");
}
if (PropertyChanged != null) {
PropertyChanged(this, e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Window1 : Window
{
private class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) {
return null;
} else if (value is int) {
return (((int)value) + 15).ToString();
} else {
return "no int";
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
private class InnerVM : VMBase
{
private int myValue;
public int MyValue {
get {
return myValue;
}
set {
if (myValue != value) {
myValue = value;
OnPropertyChanged("MyValue");
}
}
}
}
private class OuterVM : VMBase
{
private InnerVM thing;
public InnerVM Thing {
get {
return thing;
}
set {
if (thing != value) {
thing = value;
OnPropertyChanged("Thing");
}
}
}
}
private readonly OuterVM vm = new OuterVM();
public Window1()
{
InitializeComponent();
var txt = new TextBlock();
txt.SetBinding(TextBlock.TextProperty,
new Binding("Thing.MyValue") {
Source = vm,
Mode = BindingMode.OneWay,
Converter = new MyConverter(),
TargetNullValue = "(error)"
});
container.Content = txt;
var txt2 = new TextBlock();
txt2.SetBinding(TextBlock.TextProperty,
new Binding("Thing") {
Source = vm,
Mode = BindingMode.OneWay,
Converter = new MyConverter(),
TargetNullValue = "(error)"
});
container2.Content = txt2;
}
void Button_Click(object sender, RoutedEventArgs e)
{
vm.Thing = new InnerVM();
vm.Thing.MyValue += 10;
}
}
}
Window1.xaml:
<Window x:Class="NPCPropertyPath.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NPCPropertyPath" Height="300" Width="300">
<StackPanel>
<Button Content="Change value" Click="Button_Click"/>
<ContentControl Name="container"/>
<ContentControl Name="container2"/>
</StackPanel>
</Window>
当然,这是我的实际应用程序的一种显着简化的形式,其中还有很多事情要做,并且所涉及的类不会挤在两个文件中(甚至在同一个程序集中),它们都可以互相看到。
此示例显示一个带有一个按钮和两个内容控件的窗口。每个内容控件都包含一个TextBlock
其Text
属性绑定到视图模型的属性。视图模型实例(类型OuterVM
)被分配给每个绑定的Source
属性。
OuterVM
实现INotifyPropertyChanged
;它有一个Thing
类型的属性InnerVM
(也实现INotifyPropertyChanged
了),而后者又具有一个属性MyValue
。
第一个文本块绑定到Thing.MyValue
,第二个文本块绑定到Thing
。这两个绑定都有一个转换器集,以及一个属性值,TargetNullValue
如果目标属性是 ,则应该在文本块上显示null
。
实例的Thing
属性最初是. 单击按钮时,会为该属性分配一些内容。OuterVM
null
问题:只有第二个文本块最初显示任何内容。绑定的那个Thing.MyValue
既不调用转换器(如设置断点所证明的那样),也不使用TargetNullValue
属性的值。
为什么?以及如何让第一个文本块显示默认值而不是Thing.MyValue
whileThing
未分配?