Свойства POCO привязки данных
Существуют ли какие-либо структуры привязки данных (BCL или иные), которые позволяют связывать любые два свойства CLR, которые реализуют INotifyPropertyChanged
а также INotifyCollectionChanged
? Кажется, должно быть возможно сделать что-то вроде этого:
var binding = new Binding();
binding.Source = someSourceObject;
binding.SourcePath = "Customer.Name";
binding.Target = someTargetObject;
binding.TargetPath = "Client.Name";
BindingManager.Bind(binding);
куда someSourceObject
а также someTargetObject
это просто POCO, которые реализуют INotifyPropertyChanged
, Тем не менее, я не знаю о какой-либо поддержке BCL для этого, и я не уверен, что существуют существующие платформы, которые позволяют это.
ОБНОВЛЕНИЕ: Учитывая, что нет доступной библиотеки, я взял на себя ответственность написать свою собственную. Это доступно здесь.
Спасибо
6 ответов
Я не знаю ни одной библиотеки, которая бы делала это, но вы могли бы написать свою собственную довольно легко.
Вот основа, которую я обнаружил за несколько минут, которая устанавливает двухстороннее связывание данных между двумя простыми свойствами:
public static class Binder
{
public static void Bind(
INotifyPropertyChanged source,
string sourcePropertyName,
INotifyPropertyChanged target,
string targetPropertyName)
{
var sourceProperty
= source.GetType().GetProperty(sourcePropertyName);
var targetProperty
= target.GetType().GetProperty(targetPropertyName);
source.PropertyChanged +=
(s, a) =>
{
var sourceValue = sourceProperty.GetValue(source, null);
var targetValue = targetProperty.GetValue(target, null);
if (!Object.Equals(sourceValue, targetValue))
{
targetProperty.SetValue(target, sourceValue, null);
}
};
target.PropertyChanged +=
(s, a) =>
{
var sourceValue = sourceProperty.GetValue(source, null);
var targetValue = targetProperty.GetValue(target, null);
if (!Object.Equals(sourceValue, targetValue))
{
sourceProperty.SetValue(source, targetValue, null);
}
};
}
}
Конечно, в этом коде не хватает нескольких тонкостей. Что добавить
- Проверяя что
source
а такжеtarget
назначены - Проверка того, что свойства определены
sourcePropertyName
а такжеtargetPropertyName
существовать - Проверка совместимости типов между двумя свойствами
Кроме того, Reflection относительно медленный (хотя тестируйте его перед тем, как выбросить, он не такой медленный), поэтому вы можете использовать вместо этого скомпилированные выражения.
Наконец, учитывая, что указание свойств в виде строки может привести к ошибкам, вы можете использовать вместо них выражения Linq и методы расширения. Тогда вместо того, чтобы писать
Binder.Bind( source, "Name", target, "Name")
ты мог бы написать
source.Bind( Name => target.Name);
Может быть, здесь может помочь Bindable LINQ или непрерывный linq. Если вы пытаетесь добавить свойства модели, которые на самом деле являются "производными свойствами" ваших актуальных, обновляемых данных, чтобы облегчить привязку пользовательского интерфейса, эти две платформы должны помочь.
AutoMapper может копировать значения между двумя экземплярами, но вы должны написать свой собственный код, чтобы это произошло автоматически.
Я написал небольшой проект Bind с полной поддержкой связывания между необходимыми свойствами асинхронных действий связывания. Синтаксис не может быть проще:
//Two way binding between neasted properties:
Bind.TwoWay(()=> client.Area.Data.Name == this.AreaName);
//On change action execute:
Bind
.OnChange(()=> client.Personal.Name)
.Do(x => clientName = x);
Если вы определили свои свойства как DependencyProperty, вы можете сделать это. И у WF, и у WPF есть реализация (первая ссылка для WPF. Для WF именно эта), поэтому вам нужно решить, какую использовать, но и того, и другого должно хватить для ваших нужд.