Подтвердить что ты не робот

Как разрешить связанный объект из bindingexpression с WPF?

Привет всем знает, есть ли какие-либо встроенные классы для разрешения связанного объекта из выражения привязки и это DataItem и путь свойства?

Я пытаюсь написать поведение Blend 3 для текстовых полей, которые автоматически вызывают методы для объекта, связанного с текстовым свойством Text.

Текстовое поле привязано к свойству класса viewmodel. То, что я хочу сделать, это разрешить класс viewmodel из выражения привязки, а затем выполнить вызовы.

Сначала я извлекаю выражение привязки из объекта, связанного с поведением, следующим образом:

private BindingExpression GetTextBinding()
{
    return this.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
}

Сделав это, если мы посмотрим на выражение привязки, мы увидим, что он имеет ссылку на контекст данных с помощью свойства DataItem выражения привязки.

Кроме того, у нас есть относительный путь свойства, связанного с привязкой родительского выражения привязки.

Итак, мы можем получить эту информацию:

var bindingExpression = GetTextBinding();
object dataContextItem = bindingExpression.DataItem;
PropertyPath relativePropertyPath = bindingExpression.ParentBinding.Path;

Теперь этот путь свойства потенциально может быть глубоко вложенным и сложным путем, который мне очень хотелось бы избежать (повторного) выполнения разрешения. Я искал вокруг документации .NET и отскакивал вокруг сборок с отражателем, но ничего не имею - я не могу найти то, что обязательно должно существовать - должен быть какой-то класс, который выполняет разрешение пути для dataitem ( данных).

Кто-нибудь знает, где это может существовать? Любые предложения по альтернативным способам решения связанного объекта?

Обратите внимание: я пытаюсь получить связанный объект, который является родительским элементом связанного свойства (строка в этом случае) - я, очевидно, легко могу получить связанное значение, но это родительский элемент, который мне нужен.

Заранее благодарим за помощь! Фил

4b9b3361

Ответ 1

Ниже приведена краткая реализация метода расширения, который будет выполнять именно то, что вы ищете. Я тоже не мог найти ничего подобного. Метод ниже всегда возвращает значение null, если по какой-либо причине значение не может быть найдено. Метод не будет работать, если путь включает []. Надеюсь, это поможет!

public static T GetValue<T>(this BindingExpression expression, object dataItem)            
{
    if (expression == null || dataItem == null)
    {
        return default(T);
    }

    string bindingPath = expression.ParentBinding.Path.Path;
    string[] properties = bindingPath.Split('.');

    object currentObject = dataItem;
    Type currentType = null;

    for (int i = 0; i < properties.Length; i++)
    {
        currentType = currentObject.GetType();                
        PropertyInfo property = currentType.GetProperty(properties[i]);
        if (property == null)
        {                    
            currentObject = null;
            break;
        }
        currentObject = property.GetValue(currentObject, null);
        if (currentObject == null)
        {
            break;
        }
    }

    return (T)currentObject;
}

Ответ 2

Для людей в будущем, которые спотыкаются на этот вопрос:

Когда .NET 4.5 станет доступным, он будет иметь ряд новых свойств в BindingExpression, чтобы значительно упростить то, что вы ищете.

ResolvedSource. Объект, который на самом деле связан, полезен, когда у вас есть источник привязки, такой как "grandparent.parent.me.Name". Это вернет объект "я".

ResolvedSourcePropertyName. Имя свойства на связанном с ResolvedSource. В приведенном выше случае "Имя".

Аналогично, будут Target и TargetName.

С помощью этих вспомогательных свойств на BindingExpression вы можете использовать более короткое и гораздо более упрощенное отражение, которое, скорее всего, будет работать в экстремальных ситуациях (индексаторы).

Ответ 3

Для получения дополнительной информации разрешение PropertyPath обрабатывается внутренним классом MS, называемым PropertyPathWorker, который находится в PresentationFramework под MS.Internal.Data. Если вы откроете его в Reflector, вы можете увидеть его довольно сложным, поэтому я бы не рекомендовал дублировать его функциональность. Он тесно связан с общей архитектурой привязки.

Самый надежный способ поддержки всех синтаксисов пути свойств - включая прикрепленные свойства зависимостей и иерархический обход - вероятно, для создания фиктивного DependencyObject с DependencyProperty. Затем вы можете создать привязку с вашего пути "владелец" к свойству фиктивной зависимости, создать новое BindingExpression, а затем вызвать выражение UpdateTarget. Это довольно сложный способ сделать то, что на первый взгляд выглядит как простая задача, но я думаю, что есть много скрытых ошибок в пути пути доступа, которые разрешены для привязки.

Ответ 4

Я считаю, что это другое решение StackOverflow размещенное здесь, может также работать на вас.

Скопированный блок кода для справки, прочитайте оригинальное сообщение для получения дополнительной информации, предоставленной Томасом Левесом.

public static class PropertyPathHelper
{
    public static object GetValue(object obj, string propertyPath)
    {
        Binding binding = new Binding(propertyPath);
        binding.Mode = BindingMode.OneTime;
        binding.Source = obj;
        BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
        return _dummy.GetValue(Dummy.ValueProperty);
    }

    private static readonly Dummy _dummy = new Dummy();

    private class Dummy : DependencyObject
    {
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null));
    }
}

Ответ 5

Как уже отмечал Дэн Брайант, разрешение PropertyPath тесно связано с общей архитектурой связывания.
Если вам нужно то же разрешение, что и WPF, вы должны, вероятно, использовать ответ Томаса Левеска на этот вопрос.

Однако, если вам просто нужно разрешение общего пути, вы можете использовать пакет nuget Pather.CSharp Я разработал, что делает именно это.

По сути, он похож на ответ на геха, но более сложный.

Его основной метод Resolve в классе Resolver. Передача в целевом объекте и путь, поскольку строка возвращает желаемый результат.
Пример:

IResolver resolver = new Resolver(); 
var target = new { Property1 = new { Property2 = "value" } };   
object result = r.Resolve(target, "Property1.Property2");

Он также поддерживает доступ к коллекции через индекс или доступ к словарю через ключ.
Примеры путей для них:

"ArrayProperty[5]"
"DictionaryProperty[Key]"