我正在尝试将依赖项属性绑定到具有多级属性路径的INotifyPropertyChanged
启用的属性。
当属性的所有者对象不是null
时,绑定有效,但如果所有者对象为null
,则绑定不会执行任何操作(转换器不会被调用,{{3不使用)。
以下是一些重现问题的最小示例代码:
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>
当然,这是我的真实应用程序的一个显着简化的形式,其中有相当多的事情发生并且所涉及的类没有被塞在两个文件中(甚至在同一个程序集中)所有人都能看到对方。
此示例显示一个带有按钮和两个内容控件的窗口。每个内容控件都包含TargetNullValue
,其TextBlock
绑定到视图模型中的属性。视图模型实例(类型OuterVM
)被分配给每个绑定的Text
property。
OuterVM
实施INotifyPropertyChanged
;它有Thing
类型的属性InnerVM
(也实现了INotifyPropertyChanged
),后者又有一个属性MyValue
。
第一个文本块绑定到Thing.MyValue
,第二个文本块绑定到Thing
。两个绑定都有一个转换器集,以及TargetNullValue
属性的值,如果目标属性为null
,则该值应显示在文本块上。
Thing
实例的OuterVM
属性最初为null
。单击按钮时,会为该属性分配一些内容。
问题:只有第二个文本块最初显示任何内容。绑定到Thing.MyValue
的那个既不调用转换器(通过设置断点来证明),也不使用TargetNullValue
属性的值。
为什么呢?如果未分配Thing.MyValue
,我怎样才能让第一个文本块显示默认值而不是Thing
?
答案 0 :(得分:2)
为此,您不应使用TargetNullValue
,应使用Binding
的{{3}}属性。