【问题标题】:ContentControl won't use DataTemplatesContentControl 不会使用 DataTemplates
【发布时间】:2015-11-01 18:40:46
【问题描述】:

我有两个模型,我想将它们表示为目录和文件。这两个模型都实现了一个包含属性string Name {get;}的接口。

public interface INode
{
    string Name { get; }
    string Path { get; }
}

public class Directory : INode
{
    public Directory(string name, string path)
    {
        this.Name = name;
        this.Path = path;
    }

    public string Name { get; }

    public string Path { get; }
}

public class File : INode
{
    public File(string name, string path)
    {
        this.Name = name;
        this.Path = path;
    }

    public string Name { get; }

    public string Path { get; }
}

我创建了两个DataTemplates,每个INode 实现一个。每个模板都有一个ContentControl 来呈现Path,以及一个TextBlock 来呈现INode.Name 属性。

<views:NavigationAwarePage
    x:Class="OpenTasks.Views.ProviderDirectoryBrowserPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="using:OpenTasks.Views"
    xmlns:models="using:OpenTasks.DomainLogic">

    <views:NavigationAwarePage.Resources>

        <DataTemplate x:Key="FolderIconTemplate">
            <Path Data="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" 
                          Fill="{StaticResource AppTint}" 
                          HorizontalAlignment="Stretch"
                          VerticalAlignment="Stretch"
                          Width="96"
                          Height="96">
                <Path.RenderTransform>
                    <ScaleTransform ScaleX="2" ScaleY="2" />
                </Path.RenderTransform>
            </Path>
        </DataTemplate>
        <DataTemplate x:Key="FileIconTemplate">
            <Path Data="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M11,4H6V20H11L18,20V11H11V4Z"
                          Fill="{StaticResource AppTint}" 
                          HorizontalAlignment="Stretch"
                          VerticalAlignment="Stretch"
                          Width="96"
                          Height="96">
                <Path.RenderTransform>
                    <ScaleTransform ScaleX="2" ScaleY="2" />
                </Path.RenderTransform>
            </Path>
        </DataTemplate>

        <DataTemplate x:DataType="models:Directory"
                              x:Key="DirectoryItemTemplate">
            <StackPanel Orientation="Horizontal">
                <ContentControl Content="{StaticResource FolderIconTemplate}" />
                <TextBlock Text="{Binding Path=Name}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:DataType="models:File"
                              x:Key="FileItemTemplate">
            <StackPanel Orientation="Horizontal">
                <ContentControl Content="{StaticResource FileIconTemplate}" />
                <TextBlock Text="{Binding Path=Name}" />
            </StackPanel>
        </DataTemplate>
    </views:NavigationAwarePage.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListView ItemsSource="{Binding Path=ContentsOfPath}" />
    </Grid>
</views:NavigationAwarePage>

然后我将ListView 数据绑定到List&lt;INode&gt; 集合。然而问题是ListView 只是呈现INode 实现的ToString() 版本,而不是使用DataTemplate

为什么ListView 不能正确使用我定义的DataTemplates 之一?我还整理了一个DataTemplateSelector,但 ListView 仍然没有使用我的模板之一。

public class DirectoryListingTemplateSelector : DataTemplateSelector
{
    public DataTemplate DirectoryTemplate { get; set; }
    public DataTemplate FileTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        if (item is File)
        {
            return FileTemplate;
        }

        return DirectoryTemplate;
    }
}

<views:NavigationAwarePage.Resources>
    <views:DirectoryListingTemplateSelector x:Key="DirectoryListingSelector" 
                                        FileTemplate="{StaticResource FileItemTemplate}"
                                        DirectoryTemplate="{StaticResource DirectoryItemTemplate}" />


<ListView ItemsSource="{Binding Path=ContentsOfPath}"
            ItemTemplateSelector="{StaticResource DirectoryListingSelector}" />

UWP 应用程序处理 DataTemplates 的方式是否与 WPF 不同?我注意到我无法在不指定键的情况下定义模板,即使我指定了DataType。我不确定可能存在哪些其他差异导致我遇到此问题。这个确切的例子在 WPF 应用程序中对我来说很好,所以这个问题特定于UWP

编辑

我不确定为什么我第一次尝试时它没有工作,但模板选择器现在工作了。我不想为每个可以支持多个模板的ContentControlItemsControl 编写模板选择器。模板选择器是进入UWP 的唯一途径吗?

<views:NavigationAwarePage
    x:Class="OpenTasks.Views.ProviderDirectoryBrowserPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="using:OpenTasks.Views"
    xmlns:models="using:OpenTasks.DomainLogic"
    xmlns:viewModels="using:OpenTasks.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DataContext="viewModels:ProviderDirectoryBrowserViewModelDesignData">

    <views:NavigationAwarePage.Resources>
        <views:DirectoryListingTemplateSelector x:Key="DirectoryListingSelector" 
                                                FileTemplate="{StaticResource FileItemTemplate}"
                                                DirectoryTemplate="{StaticResource DirectoryItemTemplate}" />

        <DataTemplate x:Key="FolderIconTemplate">
            <Path Data="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" 
                  Fill="{StaticResource AppTint}" 
                  HorizontalAlignment="Stretch"
                  VerticalAlignment="Center"
                  Width="24"
                  Height="24" />
        </DataTemplate>
        <DataTemplate x:Key="FileIconTemplate">
            <Path Data="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M11,4H6V20H11L18,20V11H11V4Z"
                  Fill="{StaticResource AppTint}" 
                  HorizontalAlignment="Stretch"
                  VerticalAlignment="Center"
                  Width="24"
                  Height="24" />
        </DataTemplate>

        <DataTemplate x:DataType="models:Directory"
                      x:Key="DirectoryItemTemplate">
            <StackPanel Orientation="Horizontal">
                <ContentControl ContentTemplate="{StaticResource FolderIconTemplate}"
                                VerticalAlignment="Center"
                                Margin="0 0 10 0"/>
                <TextBlock Text="{Binding Path=Name}"
                           VerticalAlignment="Center"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:DataType="models:File"
                      x:Key="FileItemTemplate">
            <StackPanel Orientation="Horizontal">
                <ContentControl ContentTemplate="{StaticResource FileIconTemplate}" 
                                Margin="0 0 10 0"
                                VerticalAlignment="Center"/>
                <TextBlock Text="{Binding Path=Name}"
                           VerticalAlignment="Center"/>
            </StackPanel>
        </DataTemplate>
    </views:NavigationAwarePage.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListView ItemsSource="{Binding Path=ContentsOfPath}"
                  ItemTemplateSelector="{StaticResource DirectoryListingSelector}" />
    </Grid>
</views:NavigationAwarePage>

【问题讨论】:

    标签: c# xaml datatemplate win-universal-app uwp


    【解决方案1】:

    我想说有几种方法可以解决这个问题。在您的情况下,我很想使用自定义转换器并将您的图标模板分配给它。

    转换器可能如下所示:

    public class FileIconConverter : IValueConverter
    {
        public DataTemplate FileIconTemplate { get; set; }
    
        public DataTemplate FolderIconTemplate { get; set; }
    
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            if (value is Directory)
            {
                return this.FolderIconTemplate;
            }
    
            if (value is File)
            {
                return this.FileIconTemplate;
            }
    
            return null;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
    

    然后,在页面资源中,创建转换器并分配图标模板:

    <local:FileIconConverter x:Key="FileIconConverter">
        <local:FileIconConverter.FileIconTemplate>
            <DataTemplate>
                <Path Data="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M11,4H6V20H11L18,20V11H11V4Z"
                        Fill="{StaticResource AppTint}" 
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Width="96"
                        Height="96">
                    <Path.RenderTransform>
                        <ScaleTransform ScaleX="2" ScaleY="2" />
                    </Path.RenderTransform>
                </Path>
            </DataTemplate>
        </local:FileIconConverter.FileIconTemplate>
        <local:FileIconConverter.FolderIconTemplate>
            <DataTemplate>
                <Path Data="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" 
                            Fill="{StaticResource AppTint}" 
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            Width="96"
                            Height="96">
                    <Path.RenderTransform>
                        <ScaleTransform ScaleX="2" ScaleY="2" />
                    </Path.RenderTransform>
                </Path>
            </DataTemplate>
        </local:FileIconConverter.FolderIconTemplate>
    </local:FileIconConverter>
    

    最后,在您的列表视图中,绑定您的数据并使用转换器:

    <ListView x:Name="ListView">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <ContentControl ContentTemplate="{Binding Converter={StaticResource FileIconConverter}}" 
                                Margin="0 0 10 0"
                                VerticalAlignment="Center"/>
                    <TextBlock Text="{Binding Path=Name}"
                            VerticalAlignment="Center"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    【讨论】:

    • 这与使用DataTemplateSelector 有何不同?我想知道为什么你会使用转换来处理模板之间的切换,通过模板选择器
    • 两个原因:1/ 性能:使用 datatemplateselector 会杀死项目回收,因为它会阻止缓存 (msdn.microsoft.com/en-us/library/windows/apps/…),尽管可以说我的解决方案也需要实现模板,即使它们更小,2 / 可维护性:我没有复制主要项目模板,因此添加更多图标类型更容易。您可以通过绑定路径的Data 而不是整个控件来进一步推动这个概念
    猜你喜欢
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 2012-03-13
    • 2018-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多