我可以在XAML(.NET 4框架之前)中指定泛型类型吗?

75
在XAML中,我可以声明一个DataTemplate,以便在显示特定类型时使用该模板。例如,这个DataTemplate将使用TextBlock来显示客户的名称:
<DataTemplate DataType="{x:Type my:Customer}">
    <TextBlock Text="{Binding Name}" />
</DataTemplate>

我想知道是否可以定义一个DataTemplate,使得任何时候显示IList<Customer>时都会使用它。因此,如果ContentControl的Content是ObservableCollection<Customer>,它将使用该模板。

在XAML中是否可以使用{x:Type}标记扩展声明像IList这样的通用类型?


您实际上有两个问题,首先是DataTemplate不支持接口,第二个是泛型。 - MikeT
有关框架的最新版本,请参见https://dev59.com/-VvUa4cB1Zd3GeqPv7Bt。 - Ian Ringrose
5个回答

32

虽然不能直接在XAML中实现,但是你可以在XAML中引用DataTemplateSelector来选择正确的模板。

public class CustomerTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item,
                                                DependencyObject container)
    {
        DataTemplate template = null;
        if (item != null)
        {
            FrameworkElement element = container as FrameworkElement;
            if (element != null)
            {
                string templateName = item is ObservableCollection<MyCustomer> ?
                    "MyCustomerTemplate" : "YourCustomerTemplate";

                template = element.FindResource(templateName) as DataTemplate;
            } 
        }
        return template;
    }
}

public class MyCustomer
{
    public string CustomerName { get; set; }
}

public class YourCustomer
{
    public string CustomerName { get; set; }
}
资源字典:
<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    >
    <DataTemplate x:Key="MyCustomerTemplate">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="150"/>
            </Grid.RowDefinitions>
            <TextBlock Text="My Customer Template"/>
            <ListBox ItemsSource="{Binding}"
                     DisplayMemberPath="CustomerName"
                     Grid.Row="1"/>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="YourCustomerTemplate">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="150"/>
            </Grid.RowDefinitions>
            <TextBlock Text="Your Customer Template"/>
            <ListBox ItemsSource="{Binding}"
                     DisplayMemberPath="CustomerName"
                     Grid.Row="1"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

窗口XAML:

<Window 
    x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" 
    Height="300" 
    Width="300"
    xmlns:local="clr-namespace:WpfApplication1"
    >
    <Grid>
        <Grid.Resources>
            <local:CustomerTemplateSelector x:Key="templateSelector"/>
        </Grid.Resources>
        <ContentControl 
            Content="{Binding}" 
            ContentTemplateSelector="{StaticResource templateSelector}" 
            />
    </Grid>
</Window>

窗口背后的代码:

public partial class Window1
{
    public Window1()
    {
        InitializeComponent();
        ObservableCollection<MyCustomer> myCustomers
            = new ObservableCollection<MyCustomer>()
        {
            new MyCustomer(){CustomerName="Paul"},
            new MyCustomer(){CustomerName="John"},
            new MyCustomer(){CustomerName="Mary"}
        };

        ObservableCollection<YourCustomer> yourCustomers
            = new ObservableCollection<YourCustomer>()
        {
            new YourCustomer(){CustomerName="Peter"},
            new YourCustomer(){CustomerName="Chris"},
            new YourCustomer(){CustomerName="Jan"}
        };
        //DataContext = myCustomers;
        DataContext = yourCustomers;
    }
}

1
可能有一个更好、更简单的解决方案。 - Nerd in Training
1
我发现这比其他模板选择器教程更容易理解。谢谢。 - Jack Frost

24

目前来说,不能直接做到;不过还是有一些有创意的开发者已经做到了。

例如微软的 Mike Hillberg 在这篇文章中尝试了这个功能。当然,谷歌也有其他类似的功能。


22
您还可以将通用类包装在一个派生类中,以指定 T。
public class StringList : List<String>{}

并使用XAML中的StringList。


这对我有用。拥有一个完全空的类可能有点奇怪,但它能完成任务。 - Torben Junker Kjær
1
我使用这种技术在https://dev59.com/onA75IYBdhLWcg3weY_u#33827448中构建了一个通用的包装器。 - Ruben Bartelink

7

aelij(WPF Contrib项目的项目协调员)有另一种方法来实现它。

更酷的是(即使它在未来某个时候)... XAML 2009(XAML 2006是当前版本)将原生支持这个。查看此PDC 2008会议以获取更多信息。


5
截至.NET 4.0,WPF 4.0,XAML 2009仅在松散的xaml文件中受支持。也就是说,Blend、Cider(Visual Studio设计器)和编译后的BAML(嵌入式xaml编译成的内容)…不支持新语法。希望这一点会在将来的WPF版本中得到改变。请参见以下链接并投票:http://dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/suggestions/478860-support-xaml2009-throughout?ref=title - cplotts

0

这相当于破坏了泛型的目的,但你可以定义一个继承自泛型的类,其唯一目的是能够在XAML中使用该类型。

public class MyType : List<int> { }

并在 XAML 中使用它,例如:

<DataTemplate DataType={x:Type myNamespace:MyType}>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接