我有一个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;
// [...]
}
}
}
答案 0 :(得分:1)
您可以创建符合您需求的新CustomControl
。它将自己应用样式,您可以提供额外的DepdendencyProperties
以使其更方便。最后,您仍然可以将其放在DataTemplate
中,以便与DataTemplateSelector
一起使用。
答案 1 :(得分:1)
您可以使用XamlReader.Parse或XamlReader.Load方法动态创建DataTemplate
,例如:
string template = "<DataTemplate xmlns =\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x =\"http://schemas.microsoft.com/winfx/2006/xaml\"><StackPanel>[PLACEHOLDER]</StackPanel></DataTemplate>".Replace("[PLACEHOLDER]", "...custom code...");
return System.Windows.Markup.XamlReader.Parse(template) as DataTemplate;
自定义部件可以定义为UserControls
。
我担心在纯XAML中没有办法将DataTemplate
基于另一个。{/ p>