Сегодня у меня действительно тяжелая ситуация. Я не могу понять из-за отсутствия опыта работы с WPF. Я разрабатываю небольшие рамки для генерации графического пользовательского интерфейса (WinForms \ WPF \ HTML) из класса. Для этого мне нужно динамически создавать элементы.WPF: привязка DependencyProperty в DataTemplate. PropertyChangedCallback не уволен
У меня есть пользовательское UserControl с DependencyProperty
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 реализация.
Я проясняю этот момент BindingObject это обертка объекта, которая реализует интерфейс INotifyPropertyChange. Тип BindingObject имеет свойство Value, которое мне нужно привязать. Я могу добавить этот класс в начальный пост для большего понимания. PS. dataItem вот строка основного объекта. ObjectPickerDataTemplate - столбец со сложным типом свойства основного объекта. – CMaker
Как определяется класс dataItem и как ObjectPicker UserControl связан с этой проблемой? Вы создаете ContentControl в методе GenerateElement, не так ли? – mm8
dataItem - это переменная из параметров метода GenerateElement (protected override FrameworkElement GenerateElement (ячейка DataGridCell, объект dataItem объекта), здесь объект объекта SourceItems). Тогда свойство dataItem является сложным типом, затем я помещаю компонент ObjectPicker в ячейку ((DataTemplate) cell.FindResource (TemplateName);) – CMaker