Problémy & Řešení - DataTemplateSelector

Problém

Máme kolekci dat a chceme např. barevně vyznačit zda-li se jedná o první či poslední řádek, nebo sudý či lichý.

Řešení

Někoho by mohlo napadnout zkusit použít Trigger (+ nějaký Converter), ale podle mě je vhodnější použít tzv. DataTemplateSelector.

public class ListDataTemplateSelector : DataTemplateSelector
{
    public ObservableCollection<Item> Items { get; set; }

    public override DataTemplate SelectTemplate(object obj, DependencyObject container)
    {
        var el = container as FrameworkElement;

        if ( el != null && obj != null && obj is Item )
        {
            Item item = obj as Item;
            if ( Items.IndexOf(item)%2 == 0 )
            {
                return el.FindResource("even") as DataTemplate;
            }
            else
            {
                return el.FindResource("odd") as DataTemplate;
            }
        }

        return base.SelectTemplate(obj, container);
    }
}

Vytvoříme si vlastní třídu ListDataTemplateSelector , která dědí právě ze třídy DataTemplateSelector. Tato třída nabízí jednu metodu k přepsání SelectTemplate. Tato metoda se volá pro každý prvek v naší kolekci, kde si zjistíme zda-li se jedná o sudý či lichý prvek a vrátíme konkrétní šablonu (DataTemplate). Tyto šablony si můžeme např. definovat ve Window.Resources. Pro ukázku jsou mými prvky objekty třídy Item, která poskytuje jednu vlastnost Text a implementuje rozhraní IEquatable<Item>. Toto rozhraní tam je z důvodu, že využíváme metodu IndexOf objektu Collection<T>. V případě, že neimplementujeme toto rozhraní tak máme ještě možnost přepsat object metody Equals a GetHashCode. Jestliže neuděláme ani toto, tak nám bude metoda IndexOf vracet pořád –1.

public class Item : IEquatable<Item>
{
    public string Text { get; set; }

    public bool Equals(Item other)
    {
        return string.Compare(Text, other.Text, StringComparison.OrdinalIgnoreCase) == 0;
    }
}

V hlavním okně MainWindow.xaml si definujeme jednotlivé šablony. Pro lichý prvek to bude elipsa a pro sudý rámeček.

<Window x:Class="DataTemplateSelectorSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:template="clr-namespace:DataTemplateSelectorSample"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="even">
            <Grid>
                <Ellipse Fill="LightGreen" Width="50" Height="50"/>
                <TextBlock Text="{Binding Text}" 
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"/>
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="odd">
            <Border Background="IndianRed" Width="50" Height="50">
                <TextBlock Text="{Binding Text}" 
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"/>
            </Border>
        </DataTemplate>
        <template:ListDataTemplateSelector x:Key="templateSelector"/>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding}"
                 ItemTemplateSelector="{StaticResource templateSelector}"/>
    </Grid>
</Window>

Dále si nainicializujeme ListDataTemplateSelector, na který se potom odkážeme z ListBoxu pomocí StaticResource (při nastavování ItemTemplateSelector)

Na závěr si vygenerujeme testovací data aby jsme si ověřili, že vše funguje tak jak má.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var data = Enumerable.Range(0, 10).Select(x => new Item { Text = x.ToString() });
        this.DataContext = data;

        ListDataTemplateSelector templateSelector =
            this.FindResource("templateSelector") as ListDataTemplateSelector;
        templateSelector.Items = new ObservableCollection<Item>();
        foreach ( var item in data )
        {
            templateSelector.Items.Add(item);
        }
    }
}

Kromě nastavení DataContextu ze kterého si ListBox získá příslušnou kolekci je ještě nutné naplnit kolekci Items našeho templateSelectoru (získáme z Resources).

Výsledná aplikace vypadá následovně:

image

Ohodnoťte článek: starstarstarstarstar

Komentáře

Přidat komentář

jméno

text komentáře

opište text z obrázku


O autorovi

Lukáš Kubis

Lukáš Kubis

Působí jako Microsoft Student Partner na VŠB-TU Ostrava a zároveň pracuje jako software developer.

Mezi jeho portfolio patří platforma Microsoft .NET především WPF, WCF, Silverlight, ASP.NET MVC, ale i oblast týmového vývoje a Design Patterns.

Rovněž publikuje na MSTV.cz a je držitelem ocenění Microsoft Most Valuable Professional (MVP)

Máte-li zájem o školení, konzultaci či zakázkový vývoj pak mě neváhejte kontaktovat!

web: http://web.lukaskubis.net

blog: http://lukaskubis.net

©2010 | David Beinhauer | Lukáš Kubis