web-dev-qa-db-fra.com

Puis-je spécifier un type générique dans XAML (pré. NET 4 Framework)?

En XAML, je peux déclarer un DataTemplate afin que le modèle soit utilisé chaque fois qu'un type spécifique est affiché. Par exemple, ce DataTemplate utilisera un TextBlock pour afficher le nom d'un client:

<DataTemplate DataType="{x:Type my:Customer}">
    <TextBlock Text="{Binding Name}" />
</DataTemplate>

Je me demande s'il est possible de définir un DataTemplate qui sera utilisé chaque fois qu'un IList <Client> est affiché. Ainsi, si le contenu d'un ContentControl est, par exemple, un ObservableCollection <Client>, il utilisera ce modèle.

Est-il possible de déclarer un type générique comme IList dans XAML à l'aide de l'extension de marquage {x: Type}?

72
Matt Hamilton

Pas sorti de la boîte, non; mais il y a des développeurs entreprenants qui l'ont fait.

Mike Hillberg de Microsoft a joué avec cela dans ce post , par exemple. Google en a d'autres bien sûr.

24
ageektrapped

Pas directement dans XAML, cependant vous pouvez référencer une DataTemplateSelector de XAML pour choisir le bon modèle.

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; }
}

Le dictionnaire de ressources:

<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>

La fenêtre 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>

Le code de la fenêtre derrière:

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;
    }
}
30
Ian Oakes

Vous pouvez également envelopper votre classe générique dans une classe dérivée qui spécifie le T

public class StringList : List<String>{}

et utilisez StringList à partir de XAML.

21
Claudiu Mihaila

aelij (le coordinateur du projet pour WPF Contrib projet) a un autre moyen de le faire.

Ce qui est encore plus cool (bien que ce soit parfois dans le futur) ... c'est que XAML 2009 (XAML 2006 est la version actuelle) va supporter cela nativement. Découvrez cette PDC session 2008 pour plus d'informations.

7
cplotts

Je pense que cela peut être fait dans les versions réitérées du framework .net.

Vérifiez les Generics dans la documentation XAML . Vous devez utiliser x:TypeArguments ; il y a quelques restrictions alors lisez d'abord la documentation.

Voir aussi le Comment spécifier un argument de type générique dans la question XAML sur Stackoverflow

0
Ian Ringrose

Déforme tout à fait l'objectif d'un générique, mais vous pouvez définir une classe qui dérive du générique ainsi, dans le seul but de pouvoir utiliser ce type en XAML.

public class MyType : List<int> { }

Et utilisez-le dans xaml, par exemple comme

<DataTemplate DataType={x:Type myNamespace:MyType}>
0
Mike de Klerk