Есть ли способ использовать наследование шаблонов данных в WPF?
Возможно ли иметь DataTemplate
композиция или наследование (аналогично "BasedOn" в стилях)? Есть 2 случая, когда мне это нужно.
Для унаследованных классов: у меня есть базовый класс с несколькими унаследованными классами. Я не хочу дублировать шаблон базового класса в каждом из производных классов
DataTemplate
,Различные представления: для одного и того же класса я хочу определить табличку с данными, а затем добавить ее в соответствующий шаблон. Ex. базовый шаблон будет отображать данные в объекте, а затем мне нужны разные шаблоны, которые могут выполнять различные действия над объектом при отображении данных (наследуя базовый шаблон).
2 ответа
Единственное, что я нашел для такого рода вещей, это:
<DataTemplate x:Key="BaseClass">
<!-- base class template here -->
</DataTemplate>
<DataTemplate DataType="{x:Type app:BaseClass}">
<ContentPresenter Content="{Binding}"
ContentTemplate="{StaticResource BaseClass}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:DerivedClass}">
<StackPanel>
<ContentPresenter Content="{Binding}"
ContentTemplate="{StaticResource BaseClass}"/>
<!-- derived class extra template here -->
</StackPanel>
</DataTemplate>
По сути, это создает "общий" шаблон, на который можно ссылаться с помощью ключа (в данном случае BaseClass). Затем мы определяем реальный DataTemplate для базового класса и любых производных классов. Затем производный шаблон класса добавил бы свои собственные "вещи".
Некоторое время назад было некоторое обсуждение этого вопроса, но никто не придумал лучшего решения, которое я видел.
@Fragilerus и @Liz, на самом деле я думаю, что придумал что-то лучшее. Вот еще один подход, который не только избегает дополнительной привязки ContentPresenter, но также устраняет необходимость применения шаблона в шаблоне, поскольку разделяемый контент является прямым контентом, который устанавливается во время компиляции. Единственное, что происходит во время выполнения, это привязки, которые вы устанавливаете в прямом контенте. Таким образом, это значительно ускоряет пользовательский интерфейс по сравнению с другим решением.
<!-- Content for the template (note: not a template itself) -->
<Border x:Shared="False"
x:Key="Foo"
BorderBrush="Red"
BorderThickness="1"
CornerRadius="4">
<TextBlock Text="{Binding SomeProp}" />
</Border>
<DataTemplate x:Key="TemplateA">
<!-- Static resource - No binding needed -->
<ContentPresenter Content="{StaticResource Foo}" />
</DataTemplate>
<DataTemplate x:Key="TemplateB">
<!-- Static resource - No binding needed -->
<ContentPresenter Content="{StaticResource Foo}" />
</DataTemplate>
Важно: обязательно используйте x:Shared
Атрибут вашего общего контента или это не будет работать.
WPF'y Way
Выше сказанное говорит, что это действительно не самый удобный способ для WPF делать то, что вам нужно. Это может быть достигнуто с помощью класса DataTemplateSelector, который делает именно это... выбирает шаблон данных на основе любых установленных вами критериев.
Например, вы могли бы легко настроить тот, который ищет ваши известные типы данных и возвращает один и тот же DataTemplate для них обоих, но для всех других типов он обращается к системе для разрешения DataTemplate. Это то, что мы на самом деле делаем здесь.
Надеюсь это поможет!:)