Как связать свойство Binding.Path с базовыми данными? - PullRequest
6 голосов
/ 22 апреля 2009

Я пытаюсь связать свойство Text TextBlock очень динамичным способом. Мне нужно получить путь от базового объекта.

Вот шаблон данных:

<DataTemplate DataType={x:Type local:DummyClass}>
  <TextBlock Text={Binding Path=???} />
</DataTemplate>

У объекта DummyClass есть свойство с именем "FieldValuePath" - путь, который нужно поместить туда, где ??? является.

Идея заключается в том, что шаблон данных должен быть графическим интерфейсом для просмотра / редактирования любого свойства любого объекта. Поэтому желательно иметь возможность объявлять XAML, который будет привязывать некоторые элементы управления (текстовые поля, текстовые блоки, средства выбора даты и т. Д.) К данному свойству.

Может быть, у кого-нибудь есть предложения о том, как реализовать такую ​​вещь?

Ответы [ 3 ]

7 голосов
/ 22 апреля 2009

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

Binding binding = new Binding("BindingPath");
binding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(textBoxName, TextBox.TextProperty, binding);

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

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


Еще одна возможность, основанная на ваших комментариях:

В этом блоге описан способ создания пользовательского класса привязки путем наследования от MarkupExtension. Возможно, вы сможете использовать это в качестве отправной точки, чтобы обернуть мое предложение в повторно используемую разметку xaml для вашего специального обязательного случая.


Больше мыслей:

Хорошо, это была интересная проблема, поэтому я решил потратить немного времени на поиски рабочего решения. Заранее извиняюсь за длину следующих примеров кода ...

Основываясь на своем решении в блоге, на который я ссылался выше, я создал этот класс:

public class IndirectBinder : MarkupExtension
    {
        public string IndirectProperty { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            //try to get bound items for our custom work
            DependencyObject targetObject;
            DependencyProperty targetProperty;
            bool status = TryGetTargetItems(serviceProvider, out targetObject, out targetProperty);

            if (status)
            {
                Control targetControl = targetObject as Control;
                if (targetControl == null) return null;

                //Find the object to take the binding from
                object dataContext = targetControl.DataContext;
                if (dataContext == null) return null;

                //Reflect out the indirect property and get the value
                PropertyInfo pi = dataContext.GetType().GetProperty(IndirectProperty);
                if (pi == null) return null;

                string realProperty = pi.GetValue(dataContext, null) as string;
                if (realProperty == null) return null;

                //Create the binding against the inner property
                Binding binding = new Binding(realProperty);
                binding.Mode = BindingMode.TwoWay;
                BindingOperations.SetBinding(targetObject, targetProperty, binding);

                //Return the initial value of the binding
                PropertyInfo realPi = dataContext.GetType().GetProperty(realProperty);
                if (realPi == null) return null;

                return realPi.GetValue(dataContext, null);

            }

            return null;

        }

        protected virtual bool TryGetTargetItems(IServiceProvider provider, out DependencyObject target, out DependencyProperty dp)
        {
            target = null;
            dp = null;
            if (provider == null) return false;

            //create a binding and assign it to the target
            IProvideValueTarget service = (IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget));
            if (service == null) return false;

            //we need dependency objects / properties
            target = service.TargetObject as DependencyObject;
            dp = service.TargetProperty as DependencyProperty;
            return target != null && dp != null;
        }

Вы можете использовать эту новую разметку со следующим xaml:

<TextBox Text="{local:IndirectBinder IndirectProperty=FieldValuePath}"/>

Где TextBox может быть любым классом, который наследуется от элемента управления, а Text может быть любым свойством зависимости.

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

Несмотря на то, что это сложное решение, одним из преимуществ использования преобразователя является то, что окончательно созданная привязка направлена ​​против фактического внутреннего свойства, а не объекта. Это означает, что он правильно реагирует на события PropertyChanged.

2 голосов
/ 22 апреля 2009

Я бы порекомендовал использовать конвертер:

 <DataTemplate DataType={x:Type local:DummyClass}>
    <TextBlock Text={Binding Converter={StaticResource PropertyNameToValueConverter, ConverterParameter=FieldValuePath}} />
 </DataTemplate>

Преобразователь получит класс и имя свойства и оттуда вернет значение, используя отражение.

0 голосов
/ 22 апреля 2009
<DataTemplate DataType={x:Type local:DummyClass}>
  <TextBlock Text={Binding Path=FieldValuePath} />
</DataTemplate>

Должен быть правильный метод. Если вы прослушиваете изменения в FieldValuePath, вам необходимо убедиться, что DummyClass наследует от INotifyPropertyChanged и что при изменении FieldValuePath происходит событие изменения свойства.

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