3

我正在尝试将依赖属性绑定到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>

当然,这是我的实际应用程序的一种显着简化的形式,其中还有很多事情要做,并且所涉及的类不会挤在两个文件中(甚至在同一个程序集中),它们都可以互相看到。

此示例显示一个带有一个按钮和两个内容控件的窗口。每个内容控件都包含一个TextBlockText属性绑定到视图模型的属性。视图模型实例(类型OuterVM)被分配给每个绑定的Source属性

OuterVM实现INotifyPropertyChanged;它有一个Thing类型的属性InnerVM(也实现INotifyPropertyChanged了),而后者又具有一个属性MyValue

第一个文本块绑定到Thing.MyValue,第二个文本块绑定到Thing。这两个绑定都有一个转换器集,以及一个属性值,TargetNullValue如果目标属性是 ,则应该在文本块上显示null

实例的Thing属性最初是. 单击按钮时,会为该属性分配一些内容。OuterVMnull

问题:只有第二个文本块最初显示任何内容。绑定的那个Thing.MyValue既不调用转换器(如设置断点所证明的那样),也不使用TargetNullValue属性的值。

为什么?以及如何让第一个文本块显示默认值而不是Thing.MyValuewhileThing未分配?

4

1 回答 1

4

为此你不应该使用TargetNullValue,你应该使用FallbackValue 的属性Binding

于 2013-12-17T09:21:24.297 回答