【问题标题】:Combining DataTemplates at runtime在运行时组合 DataTemplate
【发布时间】: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


    【解决方案1】:

    您可以使用XamlReader.ParseXamlReader.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

    恐怕没有办法将DataTemplate 建立在纯 XAML 中的另一个基础上。

    【讨论】:

    • 在详细阅读了 XamlParser 之后,我发现此页面非常有用。 ikriv.com/dev/wpf/DataTemplateCreation
    • 我现在正在实施解决方案。只是从经验中学到在我真正完成之前不要接受答案:)(但我会的。)
    【解决方案2】:

    您可以创建一个新的CustomControl 来满足您的需求。它将自行应用样式,您可以提供额外的DepdendencyProperties 以使其更方便。最后,您仍然可以将它放在DataTemplate 中,以便与您的DataTemplateSelector 一起使用。

    【讨论】:

    • 虽然这可能有效,但最好添加详细信息来解释您的答案。您可以在这里找到更多提示:How to Answer
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    相关资源
    最近更新 更多