Как разрешить связанный объект из выражения привязки с WPF? - PullRequest
21 голосов
/ 11 октября 2009

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

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

Текстовое поле связано со свойством класса 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 и отскочил вокруг сборок с отражателем, но все безрезультатно - я не могу найти то, что обязательно должно существовать - должен быть какой-то класс, который выполняет разрешение пути для элемента данных (контекст данных).

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

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

Заранее спасибо за любую помощь!Фил

Ответы [ 5 ]

24 голосов
/ 07 марта 2012

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

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

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

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

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

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

23 голосов
/ 18 октября 2009

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

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;
}
10 голосов
/ 30 декабря 2009

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

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

8 голосов
/ 12 августа 2011

Я полагаю, что это другое решение 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));
    }
}
2 голосов
/ 31 декабря 2015

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

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

Это по сути похоже на ответ Жека, но более изощренно.

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

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

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

"ArrayProperty[5]"
"DictionaryProperty[Key]"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...