WPF – własny układ oraz wygląd elementów dzięki ItemsPanelTemplate i DataTemplate

2012-07-02

W WPF dostępne są klasy ItemsPanelTemplate i DataTemplate, dzięki którym możemy zdefiniować własny układ oraz wygląd elementów prezentowanych przez wybraną kontrolkę. W tym temacie pokażę zastosowanie wspomnianych klas na przykładzie obiektu ListView wyświetlającego elementy kolekcji. Klasa ItemsPanelTemplate definiuje panel odpowiedzialny za sposób ułożenia poszczególnych elementów. Każda kontrolka dziedzicząca po ItemsControl (taką kontrolką jest m.in. ListView) posiada właściwość ItemsPanel, do której możemy przypisać obiekt ItemsPanelTemplate w celu zastosowania określonego w nim układu. Poszczególne kontrolki dziedziczące po ItemsControl posiadają domyślne obiekty ItemsPanelTemplate, i tak np. dla ListBox jest to VirtualizingStackPanel, dla MenuItem jest to WrapPanel, a dla StatusBar DockPanel. Z kolei klasa DataTemplate pozwala na określenie sposobu prezentacji (wyglądu) danego elementu. Kontrolki ItemsControl posiadają właściwość ItemTemplate, do której możemy przypisać obiekt DataTemplate. Dzięki temu poszczególne elementy w danej kontrolce otrzymają wygląd zdefiniowany w tym obiekcie. W przypadku kontrolki ListView z widokiem ustawionym jako GridView możemy użyć obiektu DataTemplate do określenia wyglądu elementów w danej kolumnie. Wystarczy taki obiekt przypisać do właściwości GridViewColumn.CellTemplate.

W celu prezentacji działania omawianych mechanizmów stworzyłem klasę Driver (zawiera ona informacje o kierowcy Formuły 1 wraz ze ścieżką do pliku z jego zdjęciem) oraz klasę DriversViewModel zawierającą kolekcję obiektów typu Driver:

public class Driver
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int BirthYear { get; set; }
    public string Team { get; set; }
    public string Photo { get; set; }

    public Driver(string firstName, string lastName, int birthYear,
        string team, string photo)
    {
        FirstName = firstName;
        LastName = lastName;
        BirthYear = birthYear;
        Team = team;
        Photo = photo;
    }
}

public class DriversViewModel
{
    private ObservableCollection<Driver> f1Drivers;

    public ObservableCollection<Driver> F1Drivers
    {
        get { return f1Drivers; }
    }

    public DriversViewModel()
    {
        f1Drivers = new ObservableCollection<Driver>
        {
            new Driver("Fernando", "Alonso", 1981, "Ferrari",
                @"D:\Photo\AlonsoFernando.jpg"),
            new Driver("Jenson", "Button", 1980, "McLaren",
                @"D:\Photo\ButtonJenson.jpg"),
            new Driver("Lewis", "Hamilton", 1985, "McLaren",
                @"D:\Photo\HamiltonLewis.jpg"),
            new Driver("Kimi", "Raikkonen", 1979, "Lotus",
                @"D:\Photo\RaikkonenKimi.jpg"),
            new Driver("Michael", "Schumacher", 1969, "Mercedes",
                @"D:\Photo\SchumacherMichael.jpg"),
            new Driver("Sebastian", "Vettel", 1987, "Red Bull",
                @"D:\Photo\VettelSebastian.jpg"),
            new Driver("Felipe", "Massa", 1981, "Ferrari",
                @"D:\Photo\MassaFelipe.jpg")
        };
    }
}

Teraz możemy utworzyć okno zawierające kontrolkę ListView z widokiem ustawionym na GridView. Kontrolka wyświetla elementy kolekcji f1Drivers:

DriversView1.xaml.cs:

/// <summary>
/// Interaction logic for DriversView1.xaml
/// </summary>
public partial class DriversView1 : Window
{
    public DriversView1()
    {
        InitializeComponent();
        this.DataContext = new DriversViewModel();
    }
}

DriversView1.xaml:

<Window x:Class="WPFDataTemplate.DriversView1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DriversView1" WindowStartupLocation="CenterScreen" Width="480" Height="550">
    <Window.Resources>
        <DataTemplate x:Key="PhotoCell">
            <Image Height="60" Stretch="Uniform" Source="{Binding Photo}" />
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListView HorizontalAlignment="Stretch" Margin="0" Name="listView1"
                  VerticalAlignment="Stretch" ItemsSource="{Binding F1Drivers}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="80" Header=""
                                    CellTemplate="{StaticResource PhotoCell}" />
                    <GridViewColumn Width="85" Header="Imię"
                                    DisplayMemberBinding="{Binding FirstName}" />
                    <GridViewColumn Width="95" Header="Nazwisko"
                                    DisplayMemberBinding="{Binding LastName}" />
                    <GridViewColumn Width="80" Header="Rocznik"
                                    DisplayMemberBinding="{Binding BirthYear}" />
                    <GridViewColumn Width="80" Header="Zaspół"
                                    DisplayMemberBinding="{Binding Team}" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

W powyższym przykładzie obiekt DataTemplate użyty został do określenia sposobu prezentacji danych w pierwszej kolumnie kontrolki. Obiekt ten (PhotoCell) został zdefiniowany w zasobach okna i składa się z elementu Image. Przypisanie tego obiektu do właściwości CellTemplate pierwszej kolumny spowoduje wyświetlenie w niej zdjęcia danego kierowcy. Oto wygląd stworzonego okna:

Domyślnie kontrolka ListView wyświetla elementy jeden pod drugim. Dzięki użyciu ItemsPanelTemplate możemy to zmienić i zdefiniować własny układ. Poniższy kod tworzy okno z kontrolką ListView, w której elementy wyświetlane są od lewej do prawej w trzech kolumnach. Dodatkowo każdy z elementów otrzymał wygląd zdefiniowany w obiekcie DataTemplate:

DriversView2.xaml.cs:

/// <summary>
/// Interaction logic for DriversView2.xaml
/// </summary>
public partial class DriversView2 : Window
{
    public DriversView2()
    {
        InitializeComponent();
        this.DataContext = new DriversViewModel();
    }
}

DriversView2.xaml:

<Window x:Class="WPFDataTemplate.DriversView2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DriversView2" WindowStartupLocation="CenterScreen" Width="585" Height="605">
    <Grid>
        <ListView Padding="10" ItemsSource="{Binding F1Drivers}">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="3" />
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Border CornerRadius="15" BorderThickness="5" BorderBrush="Navy"
                            Background="AliceBlue" Width="170" Height="170" Padding="0">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="80" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*" />
                                <RowDefinition Height="28" />
                            </Grid.RowDefinitions>
                            <Image Grid.Row="0" Grid.Column="0" Margin="7"
                                   Stretch="Uniform" Source="{Binding Photo}" />
                            <StackPanel Grid.Row="0" Grid.Column="1" Margin="0,20,0,20">
                                <Label Content="{Binding FirstName}" />
                                <Label Content="{Binding LastName}" />
                                <Label Content="{Binding BirthYear}" />
                            </StackPanel>
                            <Label Grid.Row="1" Grid.ColumnSpan="2" Width="120"
                                   Background="Navy" FontSize="14" Foreground="White"
                                   HorizontalContentAlignment="Center"
                                   Content="{Binding Team}" />
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Window>

Analogiczny kod z umieszczeniem obiektów ItemsPanelTemplate i DataTemplate w zasobach okna:

<Window x:Class="WPFDataTemplate.DriversView2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DriversView2" WindowStartupLocation="CenterScreen" Width="585" Height="605">
    <Window.Resources>
        <ItemsPanelTemplate x:Key="myItemsPanelTemplate">
            <UniformGrid Columns="3" />
        </ItemsPanelTemplate>
        <DataTemplate x:Key="myDataTemplate">
            <Border CornerRadius="15" BorderThickness="5" BorderBrush="Navy"
                    Background="AliceBlue" Width="170" Height="170" Padding="0">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="80" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="28" />
                    </Grid.RowDefinitions>
                    <Image Grid.Row="0" Grid.Column="0" Margin="7"
                           Stretch="Uniform" Source="{Binding Photo}" />
                    <StackPanel Grid.Row="0" Grid.Column="1" Margin="0,20,0,20">
                        <Label Content="{Binding FirstName}" />
                        <Label Content="{Binding LastName}" />
                        <Label Content="{Binding BirthYear}" />
                    </StackPanel>
                    <Label Grid.Row="1" Grid.ColumnSpan="2" Width="120"
                           Background="Navy" FontSize="14" Foreground="White"
                           HorizontalContentAlignment="Center"
                           Content="{Binding Team}" />
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListView Padding="10"
                  ItemsSource="{Binding F1Drivers}"
                  ItemsPanel="{StaticResource myItemsPanelTemplate}"
                  ItemTemplate="{StaticResource myDataTemplate}"/>
    </Grid>
</Window>

W powyższym przykładzie obiekt DataTemplate jest już bardziej rozbudowany niż poprzednio i dzięki niemu poszczególne elementy ListView otrzymały zupełnie inny wygląd. Z kolei użyty w ItemsPanelTemplate obiekt UniformGrid pozwolił na określenie dla nich nowego układu. Oczywiście w tym celu możemy posłużyć się także innymi kontenerami (np. WrapPanel). Tak wygląda utworzone okno:

Więcej informacji na MSDN: Data TemplatingItemsPanelTemplateDataTemplate

Reklamy

Posted on 2012-07-02, in .NET/C# and tagged , , , , , . Bookmark the permalink. 3 Komentarze.

  1. Jak zmienić dynamicznie właściwość Columns UniformGrid, gdy jest w ItemPanelTemplate ?

    • Poniżej dwie możliwości, pierwsza przy użyciu klasy FrameworkElementFactory, druga poprzez dynamiczne wczytanie kodu XAML:

      1)

      FrameworkElementFactory factory = 
      new FrameworkElementFactory(typeof(UniformGrid));
      factory.SetValue(UniformGrid.ColumnsProperty, 4);
      listView.ItemsPanel = new ItemsPanelTemplate(factory);
      

      2)

      StringBuilder xamlCode = new StringBuilder();
      xamlCode.Append("<ItemsPanelTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">");
      xamlCode.Append("<UniformGrid Columns=\"4\"/>");
      xamlCode.Append("</ItemsPanelTemplate>");            
      listView.ItemsPanel = 
      (ItemsPanelTemplate)XamlReader.Parse(xamlCode.ToString());
      

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s

%d blogerów lubi to: