Kann ich in XAML (vor .NET 4 Framework) einen generischen Typ angeben?

73

In XAML kann ich eine DataTemplate deklarieren, sodass die Vorlage immer dann verwendet wird, wenn ein bestimmter Typ angezeigt wird. In dieser DataTemplate wird beispielsweise ein TextBlock verwendet, um den Namen eines Kunden anzuzeigen:

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

Ich frage mich, ob es möglich ist, eine DataTemplate zu definieren, die jedes Mal verwendet wird, wenn eine IList <Kunde> angezeigt wird. Wenn der Inhalt eines ContentControls beispielsweise eine ObservableCollection <Kunde> ist, wird diese Vorlage verwendet.

Ist es möglich, einen generischen Typ wie IList in XAML mit der Markup-Erweiterung {x: Type} zu deklarieren?

Matt Hamilton
quelle
Sie haben tatsächlich 2 Probleme hier, erstens unterstützt DataTemplate keine Schnittstellen, die zweite ist Generics
MikeT
Aktuelle Versionen des Frameworks finden Sie unter stackoverflow.com/questions/7573712/…
Ian Ringrose

Antworten:

24

Nicht sofort einsatzbereit, nein; Aber es gibt unternehmungslustige Entwickler, die dies getan haben.

Mike Hillberg von Microsoft hat zum Beispiel in diesem Beitrag damit gespielt. Google hat natürlich andere.

ageektrapped
quelle
31

Nicht direkt in XAML, Sie können jedoch auf eine DataTemplateSelectorvon XAML verweisen , um die richtige Vorlage auszuwählen.

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

Das Ressourcenwörterbuch:

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

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

Der Fenstercode dahinter:

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;
    }
}
Ian Oakes
quelle
Wahrscheinlich eine bessere und einfachere Lösung
Nerd im Training
Ich finde das leichter zu verstehen als andere Tutorials zur Vorlagenauswahl. Vielen Dank
Jack Frost
21

Sie können Ihre generische Klasse auch in eine abgeleitete Klasse einschließen, die das T angibt

public class StringList : List<String>{}

und verwenden Sie StringList von XAML.

Claudiu Mihaila
quelle
Das hat bei mir funktioniert. Es ist ein bisschen seltsam, eine völlig leere Klasse zu haben, aber es erledigt den Job.
TJKjaer
1
Ich habe einen generischen Wrapper mit dieser Technik in stackoverflow.com/a/33827448/11635
Ruben Bartelink
7

aelij (der Projektkoordinator für das WPF Contrib- Projekt) hat eine andere Möglichkeit , dies zu tun.

Was noch cooler ist (obwohl es irgendwann in der Zukunft nicht mehr möglich ist) ... ist, dass XAML 2009 (XAML 2006 ist die aktuelle Version) dies nativ unterstützen wird. In dieser PDC 2008-Sitzung finden Sie Informationen dazu und mehr.

cplotts
quelle
5
XAML 2009 wird nur in losen xaml-Dateien unterstützt (ab .NET 4.0, WPF 4.0). Das heißt, Blend, Cider (der Visual Studio-Designer) und kompilierte BAML (in die Ihre eingebettete xaml kompiliert wird) ... unterstützen die neue Syntax nicht. Hoffentlich wird sich dies in einer zukünftigen Version von WPF ändern. Siehe den folgenden Link und stimmen Sie ab: dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/…
cplotts
0

Der Zweck eines Generikums ist ziemlich zunichte gemacht, aber Sie können eine Klasse definieren, die sich wie folgt vom Generikum ableitet, mit dem einzigen Zweck, diesen Typ in XAML verwenden zu können.

public class MyType : List<int> { }

Und benutze es in xaml zB wie

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