WPF:在DataTemplate中绑定DependencyProperty。 PropertyChangedCallback未被触发

时间:2017-01-18 11:16:02

标签: c# wpf binding

今天我的情况非常艰难。由于缺乏WPF的经验,我无法想象。我从类开发用于GUI生成的小框架(WinForms \ WPF \ HTML)。为此,我需要动态创建元素。

我使用DependencyProperty自定义UserControl

public partial class ObjectPicker : UserControl
    {
        private ViewModel _vm;
        public ObjectPicker()
        {
            InitializeComponent();
        }

        public object ObjectValue
        {
            get { return GetValue(ObjectValueProperty); }
            set { SetValue(ObjectValueProperty, value); }
        }

        public static DependencyProperty ObjectValueProperty =
            DependencyProperty.Register(
                "ObjectValue",
                typeof(object),
                typeof(ObjectPicker),
                new FrameworkPropertyMetadata(
                    new PropertyMetadata(new object(), OnObjectChangeNotify, CoerceValueCallback), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        private static void OnObjectChangeNotify(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as ObjectPicker;
            sender.OnObjectValueChange(d, e);
        }

        public void OnObjectValueChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            this.ObjectValue = e.NewValue;
        }
    }

我有CustomBoundColumn,我覆盖了GenerateElement方法(其中BindingObject - 包含2个属性Value和PropName的包装器)。然后我像这样绑定对象,然后它不起作用。

        var content = new ContentControl();
        content.ContentTemplate = (DataTemplate)cell.FindResource(TemplateName);
        var propName = ((Binding)Binding).Path.Path;
        BindingObject bo = new BindingObject(propName, dataItem);
        content.SetValue(ContentControl.ContentProperty, bo);
        return content;

数据模板(例如我放了另外两个完美的模板):

    <DataTemplate x:Key="TextBoxDataTemplate">
        <TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
    <DataTemplate x:Key="DatePickerDataTemplate">
        <DatePicker SelectedDate="{Binding Value,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
    <DataTemplate x:Key="ObjectPickerDataTemplate">
        <local:ObjectPicker ObjectValue="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </DataTemplate>

当我以编程方式绑定时:

objectPicker.SetBinding(ObjectPicker.ObjectValueProperty, binding);

什么时候有效。

我不知道解决此问题需要哪些其他信息。如果你需要更多,只需留下评论即可添加。

UPDATE1:添加BindingObject类

public class BindingObject : INotifyPropertyChanged
{
    private object _value;
    private PropertyDescriptor _pd;
    private MethodInfo _method;

    public BindingObject(string propName, object value)
    {
        _method = value.GetType().GetMethods().FirstOrDefault(x => x.Name == "OnPropertyChanged");
        if (!(value is INotifyPropertyChanged) || _method == null) throw new Exception("Invalid value");
        _value = value;
        _pd = TypeDescriptor.GetProperties(_value.GetType())[propName];

        (_value as INotifyPropertyChanged).PropertyChanged += (o, e) =>
        {
            OnPropertyChanged(nameof(Value));
            OnPropertyChanged(_pd.Name);
        };
    }

    public string PropName
    {
        get { return _pd.Name; }
    }

    public object Value
    {
        get
        {
            return _pd.GetValue(_value);
        }
        set
        {
            _pd.SetValue(_value, value);

        }
    }

    private void RaisePropertyChanged()
    {
        _method.Invoke(_value, new[] { nameof(Value) });
        _method.Invoke(_value, new[] { _pd.Name });
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

UPDATE2:dataItem的实现示例

public class DataItem
{
      public virtual string Value1 { get; set; } // This type of property work fine and binding 
      public virtual int Value2 { get; set; } // This type of property work fine and binding 
      public virtual DateTime Value3 { get; set; } // This type of property work fine and binding 
      public virtual ExampleComplexObject Object { get; set; } // This type not working

}

public class ExampleComplexObject
{
      public virtual int Value1 { get; set; }
      public virtual string Value2 { get; set; }
}

在运行时,需要创建对象类型传递给工厂,其中使用INotifyPropertyChanged实现从传递类型创建代理对象。

1 个答案:

答案 0 :(得分:0)

我不确定我是否理解您的确切问题,但您可以尝试将ContentControl的Content属性设置为数据对象:

var content = new ContentControl();
content.ContentTemplate = (DataTemplate)cell.FindResource(TemplateName);
content.SetValue(ContentControl.ContentProperty, dataItem);
return content;

然后,您应该能够使用{Binding Value}绑定到DataTemplate中的Value属性。