【发布时间】:2017-06-25 23:03:22
【问题描述】:
我有一个 ListBox,它通过其 ItemSource 显示对象的数据绑定列表。因为每个对象都有特殊的显示需求,所以我定义了一个 ItemTemplateSelector,它根据对象返回适当的 DataTemplate。一切顺利。
每个对象的 DataTemplates 遵循一个通用公式,但中间包含自定义元素。例如:
<DataTemplate x:Key="collectibleTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1">
<Expander IsExpanded="True" Header="{Binding ComponentName}" Background="WhiteSmoke">
<StackPanel>
<TextBlock Margin="5,5,5,0" Text="{Binding EditDescription}" TextWrapping="Wrap" />
<!-- This is the only custom part of each template -->
<StackPanel Margin="0,10,5,0" Orientation="Horizontal">
<Label Content="Type:" />
<ComboBox Height="22" HorizontalAlignment="Left" SelectedItem="{Binding Path=CollectibleType, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource collectibleTypeFromEnum}}" />
</StackPanel>
<!-- End custom part -->
<StackPanel Margin="0,0,0,5">
<Label Content="Available Actions:" >
<Label.Style>
<Style TargetType="Label">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding EditActions.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<ItemsControl ItemsSource="{Binding EditActions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding}" Content="{Binding Title}" ToolTip="{Binding ToolTip}" Margin="5,0,5,0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Expander>
</Border>
</Grid>
</DataTemplate>
正如您所见,有很多共享的 XAML,在中间包裹了一个小的自定义部分。
其他数据模板将由其他工程师编写(他们希望为他们添加的每种新对象类型创建一个),所以我有兴趣让创建一个新的 DataTemplate 变得简单而轻松可能的。当然,不要复制整个 DataTemplate 并在中间添加自定义“东西”——但我也不倾向于将模板的部分提取为可重用部分并引用它们,因为它仍然会导致大量重复代码每个新的 DataTemplate,这意味着可能的错误和难以维护。即,这是一种更易于维护的方法,但仍然感觉不理想:
<DataTemplate x:Key="collectibleTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1">
<Expander IsExpanded="True" Header="{Binding ComponentName}" Background="WhiteSmoke">
<StackPanel>
<TextBlock Margin="5,5,5,0" Text="{Binding EditDescription}" TextWrapping="Wrap" />
<!-- This is the only custom part of each template -->
[...]
<!-- End custom part -->
<ContentPresenter Content="{StaticResource AvailableActions}" />
</StackPanel>
</Expander>
</Border>
</Grid>
</DataTemplate>
<StackPanel Margin="0,0,0,5" x:Key="AvailableActions" x:Shared="false">
<Label Content="Available Actions:" >
<Label.Style>
<!--
[Bottom half of shared XAML from the first example, offloaded here]
-->
</StackPanel>
那么:解决这个问题的最佳策略是什么? AFAIK 我坚持使用 DataTemplates,因为这是 ListBox ItemTemplateSelector 接受的唯一元素。有没有办法在 DataTemplateSelector 中创建复合 DataTemplate?我将提供所有对象共享的库存 DataTemplate,并在每个对象类型所需的自定义 XAML 位中引用 DataTemplateSelector。其他工程师会陷入这种普遍的代码行为。
不确定,这里在黑暗中摸索着是否有一种模式可以让我优雅地解决这个问题。
而且,仅供参考:我当前的 DataTemplateSelector 非常简单。这是我希望构建最终 DataTemplate 的地方,而不是简单地返回一个在 XAML 中硬编码的。
public class NodeComponentDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
if (item is CollectibleComponent)
return element.FindResource("collectibleTemplate") as DataTemplate;
// [...]
}
}
}
【问题讨论】:
标签: c# wpf xaml datatemplateselector