Univerzální WPF DataTemplateSelector

Tomáš Jecha, MVP, MCSD       21.11.2012       C#       12928 zobrazení

Pokud pracujete s technologií WPF, jistě víte, že disponuje sympatickým systémem pro vytváření šablon (Templates). Jejich účelem je usnadnit psaní komponent, kterým půjde změnit způsob zobrazování bez nutnosti měnit kód komponenty samotné. Ve WPF existují víceméně 3 základní typy šablon: ControlTemplate (změna vzhledu celé komponenty), ItemsPanelTemplate (změna vzhledu objektu, který zobrazuje další objekty) a DataTemplate (změna vzhledu zobrazování datových objektů). V tomto krátkém příspěvku se budu věnovat šablonám DataTemplates. Ty určují, jakým způsobem se zobrazí například položky v ItemsControl komponentách. Tedy například ListBox, ComboBox a podobně.

Existují i případy, kdy chceme používat více šablon pro jednu komponentu. V tu chvíli je potřeba naimplementovat DataTemplateSelector, který se postará o výběr vhodné šablony pro každou ze zobrazovaných položek.

Abych si usnadnil práci a nemusel psát pro každý takový seznam nový DataTemplateSelector, naimplementovat jsem jeden univerzální, kterému můžete přidat více šablon a typy datových položek, pro které je má využít. Pojmenoval jsem ho TypeTemplateSelector.

Podívejte se na ukázku použití. Mám dvě ViewModel třídy, jednu pro zobrazení zaměstnance a druhou pro zobrazení hardwaru a zobrazuji je v jenom seznamu, každý s jinou šablonou:

ListBox

XAML kód:

<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
    <ItemsControl.ItemTemplateSelector>
        <c:TypeTemplateSelector>
            <!-- template for PersonViewModel -->
            <c:TemplateDefinition Type="models:PersonViewModel">
                <DataTemplate>
                    <Border Margin="5" Padding="5" Background="LightBlue">
                        <TextBlock Text="{Binding FullName}" Background="LightBlue" />
                    </Border>
                </DataTemplate>
            </c:TemplateDefinition>
            <!-- template for ComputerViewModel -->
            <c:TemplateDefinition Type="models:ComputerViewModel">
                <DataTemplate>
                    <Border Margin="5" Padding="5" Background="Orange">
                        <TextBlock>
                            <Run Text="{Binding IdNumber}" />
                            <Run Text=" " />
                            <Hyperlink>
                                <Run Text="Show status" />
                            </Hyperlink>
                        </TextBlock>
                    </Border>
                </DataTemplate>
            </c:TemplateDefinition>
        </c:TypeTemplateSelector>
    </ItemsControl.ItemTemplateSelector>
</ItemsControl>

Datové objekty a získání dat:

// sample data source (place to component/window constructor)
this.DataContext = new
{
    Items = new object[] {
        new PersonViewModel() { FullName = "Tomáš Jecha" },
        new PersonViewModel() { FullName = "Karel Novák" },
        new ComputerViewModel() { IdNumber = "IC14/65435808" },
        new ComputerViewModel() { IdNumber = "IC14/65435809" },
        new PersonViewModel() { FullName = "Some other guy" },
        new ComputerViewModel() { IdNumber = "IC14/65435810" }
    }
};

// sample data classes
public class PersonViewModel
{
    public string FullName { get; set; }
}

public class ComputerViewModel
{
    public string IdNumber { get; set; }
}

A konečně kód komponenty:

[ContentProperty("Templates")]
public class TypeTemplateSelector : DataTemplateSelector
{
    public TypeTemplateSelector()
    {
        this.Templates = new TemplateDictionary();
    }

    public TemplateDictionary Templates { get; set; }
        
    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        var templateDef = this.Templates.FindTemplateForType(item.GetType());

        if (templateDef == null || templateDef.Template == null)
        {
            // not found / template not specified - use base selector
            return base.SelectTemplate(item, container);
        }

        return templateDef.Template;
    }
}

public class TemplateDictionary : List<TemplateDefinition>
{
    public TemplateDefinition FindTemplateForType(Type type)
    {
        TemplateDefinition resultItem;

        // try to find by type
        resultItem = this.FirstOrDefault(t => t.Type != null && t.Type.IsAssignableFrom(type));

        // try to find fallback value
        if(resultItem == null)
            resultItem = this.FirstOrDefault(t => t.Type == null);

        return resultItem;
    }
}

[ContentProperty("Template")]
public class TemplateDefinition : DependencyObject
{
    public Type Type
    {
        get { return (Type)GetValue(TypeProperty); }
        set { SetValue(TypeProperty, value); }
    }

    public static readonly DependencyProperty TypeProperty =
        DependencyProperty.Register("Type", typeof(Type), typeof(TemplateDefinition), new PropertyMetadata(null));

    public DataTemplate Template
    {
        get { return (DataTemplate)GetValue(TemplateProperty); }
        set { SetValue(TemplateProperty, value); }
    }

    public static readonly DependencyProperty TemplateProperty =
        DependencyProperty.Register("Template", typeof(DataTemplate), typeof(TemplateDefinition), new PropertyMetadata(null));
}

Kód samozřejmě užívejte a upravujte, jak jen uznáte za vhodné.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback