XAML.cz Magazín moderních technologií založených na XAML

ListView – styly pro sudé a liché řádky

Napsáno pro WPF od Jirka Pénzeš [23.09.2010]

Komponenta ListView, která je obsažena v základním „balíku“ komponent ve WPF, je velmi dobrá a umí toho opravdu spousty. Na druhou stranu má ale celkem zásadní nedostatek – neumí odlišit sudé řádky od lichých. Pokud tedy chceme zacházet se sudými a lichými řádky odlišně – musíme si poradit jinak.

StyleSelector

Pro změny stylu, na základě naší logiky, se používá třída StyleSelector – lépe řečeno její potomci. Tato třída patří do jmenného prostoru System.Windows.Controls. Jak to funguje? Podstatná je metoda SelectStyle. Metoda vrací ten „správný“ styl, který se bude aplikovat na prvek. Vyhodnocení, který styl má být tím návratovým, zařídí překrytí této metody naší logikou. Vstupními členy této metody jsou parametry object a DependencyObject. První člen (object) je vlastní obsah – v našem případě to – co bude řádku bindováno. Druhým parametrem metody (DependencyObject) je už samotný prvek, který bude styl aplikovat – v našem případě ListViewItem.

Naše metoda pouze vyhodnotí fakt, zda se jedná o sudý či lichý řádek, a dle výsledku vrátí styl pro sudé řádky nebo styl pro liché.

public class ListViewItemStyleSelector : StyleSelector
{
    private int i = 0; //index řádku

    public override Style SelectStyle(object item, DependencyObject container)
    {
        ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
        if (item == itemsControl.Items[0]) // nulování indexu řádku (jsme-li na začátku)
        {
            i = 0;
        }
        string styleName;
        if (i % 2 == 0) // vyhodnotíme, zda je řádek sudý či lichý
        {
            styleName = "ListViewItemStyle_SUDE";
        }
        else
        {
            styleName = "ListViewItemStyle_LICHE";
        }
        i++;
        // najdeme styl mezi zdroji a vrátime jej
        return (Style)(itemsControl.FindResource(styleName));
    }
}

To byla část, která funguje na pozadí v jazyce C #.NET. V jazyce xaml musíme připravit zmiňované styly a ListView. Naše třída hledá styly mezi statickými zdroji, musíme tedy naše styly umístit tam. Komponenta ListView má dále nastaven parametr ItemContainerStyleSelector, která očekává náš StyleSelector, nesmíme jej tedy zapomenout přidat taktéž mezi Resources.

<Window x:Class="xaml_cz_ListViewAlternativeRows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:xaml_cz_ListViewAlternativeRows"        
        Title="MainWindow" Height="300" Width="350">
    <Grid>
        <Grid.Resources>
            <Style x:Key="ListViewItemStyle_SUDE" TargetType="{x:Type ListViewItem}">
                <Setter Property="Background" Value="White"  />
            </Style>
            <Style x:Key="ListViewItemStyle_LICHE" TargetType="{x:Type ListViewItem}">
                <Setter Property="Background" Value="GhostWhite"  />
            </Style>
            <local:ListViewItemStyleSelector x:Key="ListViewItemStyleSelector"  />
        </Grid.Resources>
        <ListView x:Name="MyListView" 
                  ItemsSource="{Binding}" 
                  SelectionMode="Single" 
                  ItemContainerStyleSelector="{StaticResource ListViewItemStyleSelector}">
            <ListView.Resources>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="VerticalAlignment" Value="Center"  />
                </Style>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="50" Header="Id" 
                                    DisplayMemberBinding="{Binding Path=Id}"  />
                    <GridViewColumn Width="270" Header="Název článku" 
                                    DisplayMemberBinding="{Binding Path=Name}"  />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

Pokud bychom nyní aplikaci přeložili a spustili – fungovala by. Chybějí nám ale data, která nám řeknou, zda bylo naše snažení úspěšné. Pro demonstraci naplníme ListView anonymními objekty pouze o dvou vlastnostech (Id a Name).

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
    List<object> data = new List<object>();
    data.Add(new { Id = 1, Name = "Zpřístupnění Silverlight v HTML stránce" });
    data.Add(new { Id = 2, Name = "MVVM a design time" });
    data.Add(new { Id = 3, Name = "DataGrid – grupování, řazení a filtrování" });
    data.Add(new { Id = 4, Name = "Geometrie ve WPF - PathGeometry" });
    data.Add(new { Id = 5, Name = "Novinky ve WPF 4 – Easing funkce" });
    data.Add(new { Id = 6, Name = "Novinky ve WPF 4 – Calendar a DatePicker" });
    data.Add(new { Id = 7, Name = "Novinky ve WPF 4 – DataGrid" });
    data.Add(new { Id = 8, Name = "Geometrie ve WPF" });
    data.Add(new { Id = 9, Name = "Novinky ve WPF 4 – TextRenderingMode a ClearType hint" });
    data.Add(new { Id = 10, Name = "Novinky ve WPF 4 – TextFormattingMode" });
    MyListView.ItemsSource = data;
}

Jestliže projekt znovu spustíte, měli byste vidět následující výsledek. Jak vidíte, naše snažení bylo úspěšné a jednotlivé řádky mají odlišný styl, který se liší v barvě pozadí řádku.

xaml_Listview_alternativeRow

Závěrerem

Závěrem bych chtěl říci, že existuje i třída DataTemplateSelector, která pracuje velmi obdobně – jen nepracuje se styly, nýbrž s šablonami.

Komentáře

ukládám komentář, vyčkejte prosím..
  1. Ondrej

    Ahoj, komponenta listview umí sama o sobě rozlišovat řádky pomocí vlastnosti AlternationCount a AlternationIndex. Nasledující kód obarví sudé a liché řádky pomocí triggeru. AlternationCount lze nastavit i na vyšší číslo a rozlišovat libovolný skupiny.

    <Style TargetType="{x:Type ListViewItem}">

    <Setter Property="Background" Value="White" />

    <Style.Triggers>

    <Trigger Property="ItemsControl.AlternationIndex" Value="1">

    <Setter Property="Background" Value="LightYellow" />

    </Trigger>

    </Style.Triggers>

    </Style>

    <ListView AlternationCount="2" />

    23.09.2010 @ 08:56
  2. Ahoj, díky za upozornění - vlastnost AlternationCount jsem nějak přehlédl.

    Přeci jen si ListView poradí i s odlišením jednotlivých řádků a to i bez pomoci StyleSelectoru. Dík.

    23.09.2010 @ 11:17
  3. Vynikající článek, skoro si až říkám, kdo vymýšlí tyhle úpravy kódu musí být fakt machr.

    23.09.2010 @ 13:39

@xamlcz

  • RT @jvanrhyn: XAML, It's a bit like olives. Takes a while to get used to. But once you're used to it. It is actually pretty good. <3 XAML
  • RT @moser_christian: WPF Inspector 0.9.7 is released. It supports .NET 3.5 and 4.0 The project is now open source and available on CodeP ...
  • Jeff Handley oznámil vydání WCF RIA Services v.1.0 SP1 RTM http://bit.ly/gOgckn ke stažení na http://bit.ly/gVAXdK
  • jedna výzva pro Brno. Byl někdo z vás na přednášce o RIA v MS Akvárku? Dejte o sobě vědět. Děkuji
  • také jste uvažovali o tom, že zkusíte na projekt použít Caliburn Micro nebo naopak Prism 4? A co tak obojí, šlo by to nebo ne? Již brzy