WPF привязка данных к интерфейсу, а не к фактическому объекту – возможность литья?
Скажем, у меня есть такой интерфейс:
public interface ISomeInterface { ... }
У меня также есть пара classов, реализующих этот интерфейс;
public class SomeClass : ISomeInterface { ... }
Теперь у меня есть элементы списка ListBox WPF для ISomeInterface, используя пользовательский DataTemplate.
- Привязка данных Android с использованием тега include
- Привязать к методу в WPF?
- ItemsControl с несколькими DataTemplates для viewmodel
- Общий class наблюдаемого словаря для DataBinding / WPF C #
- WPF MVVM INotifyPropertyChanged Implementation - модель или ViewModel
Механизм привязки данных, по-видимому, не (который я смог выяснить) позволяет мне привязываться к свойствам интерфейса – он видит, что объект является объектом SomeClass, а данные отображаются только в том случае, если SomeClass должен иметь свойство bound, доступное как свойство без интерфейса.
Как я могу заставить DataTemplate действовать так, как если бы каждый объект был ISomeInterface, а не SomeClass и т. Д.?
Благодаря!
- Управление несколькими выборами с помощью MVVM
- Связывание Richtextbox wpf
- Нажатие свойств GUI только для чтения обратно в ViewModel
- Должен ли я привязываться к ICollectionView или ObservableCollection
- Обход проблемы из-за отсутствия оператора 'nameof' в C # для безопасного хранения данных по типу?
- Связывание в WPF с элементом массива, заданным свойством
- Какие подходы доступны для фиктивных данных времени разработки в WPF?
- Есть ли способ определить, где объявляется / создается привязка WPF?
Чтобы связать с явными введенными членами интерфейса, все, что вам нужно сделать, это использовать круглые скобки. Например:
неявный:
{Binding Path=MyValue}
явный:
{Binding Path=(mynamespacealias:IMyInterface.MyValue)}
Этот ответ от форумов Microsoft Беатрис Коста – MSFT стоит прочитать (довольно старый):
Команда привязки данных обсудила добавление поддержки интерфейсов некоторое время назад, но в итоге не реализовала ее, потому что мы не смогли создать хороший дизайн для нее. Проблема заключалась в том, что интерфейсы не имеют такой иерархии, как типы объектов. Рассмотрим сценарий, в котором ваш источник данных реализует как
IMyInterface1
иIMyInterface2
и у вас есть DataTemplates для обоих этих интерфейсов в ресурсах: какой DataTemplate вы думаете, что мы должны подобрать?При выполнении неявных шаблонов данных для типов объектов мы сначала пытаемся найти
DataTemplate
для точного типа, затем для его родителя, дедушки и т. Д. Для нас очень хорошо определен порядок. Когда мы говорили о добавлении поддержки интерфейсов, мы рассмотрели использование рефлексии, чтобы узнать все интерфейсы и добавить их в конец списка типов. Проблема, с которой мы столкнулись, заключалась в определении порядка интерфейсов, когда тип реализует несколько интерфейсов.Другая вещь, которую мы должны были иметь в виду, состоит в том, что reflection не так уж и дешево, и это уменьшит наш принцип для этого сценария.
Так в чем же решение? Вы не можете сделать все это в XAML, но вы можете сделать это легко с небольшим количеством кода. Свойство
ItemTemplateSelector
элементаItemsControl
можно использовать для выбора тогоDataTemplate
который вы хотите использовать для каждого элемента. В методеSelectTemplate
для вашего селектора шаблонов вы получаете в качестве параметра элемент, который вы создадите. Здесь вы можете проверить, какой интерфейс он реализует, и вернутьDataTemplate
который соответствует ему.
Короткий ответ – DataTemplate не поддерживает интерфейсы (подумайте о множественном наследовании, явном v) неявном и т. Д.). Способ, которым мы стремимся обойти это, состоит в том, чтобы расширить возможности базового classа, чтобы дать возможность специализации / обобщения DataTemplate. Это означает, что это достойное, но не обязательно оптимальное решение:
public abstract class SomeClassBase { } public class SomeClass : SomeClassBase { }
Ответ, предложенный dummyboy, является лучшим ответом (его следует проголосовать за верхний imo). У этого есть проблема, которую конструктор ему не нравится (дает ошибку «Object null не может использоваться как параметр accessor для PropertyPath), но есть хорошее обходное решение. Обходной путь состоит в том, чтобы определить элемент в datatemplate, а затем установите шаблон в метку или другой элемент управления содержимым. В качестве примера я пытался добавить изображение, подобное этому
Но он продолжал давать мне ту же ошибку. Решением было создание ярлыка и использование шаблона данных для показа моего контента
У этого есть свои недостатки, но это, кажется, работает очень хорошо для меня.
У вас есть другой вариант. Установите ключ на свой DataTemplate и укажите этот ключ в ItemTemplate. Как это:
затем укажите шаблон по ключу, где вы хотите его использовать, например:
Примечание. Вы можете использовать более сложные многочастные пути, подобные этому, если свойство интерфейса находится внутри пути:
Или напрямую с директивой Binding
.
Или при использовании нескольких свойств интерфейса вы можете переопределить DataContext локально, чтобы сделать код более удобочитаемым.
Совет. Следите за тем, чтобы случайно закончилось )}
в конце выражения Path. Глупая ошибка копирования / вставки. Я продолжаю делать.
Path="(myNameSpace:IShippingPackage.ShippingMethod)}"
Обязательно используйте Path=
Обнаружено, что если я явно не использую Path=
тогда он не сможет проанализировать привязку. Обычно я просто пишу что-то вроде этого:
Text="{Binding FirstName}"
вместо
Text="{Binding Path=FirstName}"
Но с более сложной привязкой интерфейса я обнаружил, что Path=
необходимо для исключения этого исключения:
System.ArgumentNullException: Key cannot be null. Parameter name: key at System.Collections.Specialized.ListDictionary.get_Item(Object key) at System.Collections.Specialized.HybridDictionary.get_Item(Object key) at System.ComponentModel.PropertyChangedEventManager.RemoveListener(INotifyPropertyChanged source, String propertyName, IWeakEventListener listener, EventHandler`1 handler) at System.ComponentModel.PropertyChangedEventManager.RemoveHandler(INotifyPropertyChanged source, EventHandler`1 handler, String propertyName) at MS.Internal.Data.PropertyPathWorker.ReplaceItem(Int32 k, Object newO, Object parent) at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
т.е. не делайте этого: