Eine sehr häufig gestellte Frage ist die, wie eine WPF ListBox gestyled und animiert wird. Anhand des nachfolgenden Beispiels wird gezeigt, in welchen Templates was gemacht werden kann und welche Auswirkungen dies hat. Was kann aus diesem Artikel gelernt werden?

  • Animieren von Listbox-Einträgen
  • Überschreiben des Fokuses
  • Styling eines ausgewählten Eintrags

Um die Daten anzuzeigen, werden Objekte vom Typ Person verwendet. Dabei handelt es sich um Datenobjekte, welche sehr einfach aufgebaut sind:

public class Person
{
   public String FirstName { get; set; }
   public String LastName { get; set; }

   public override string ToString()
   {
       return String.Format("{0}, {1}", LastName, FirstName);
   }
}

Um Testdaten zu beziehen wird ein Mock-Objekt verwendet:

public static class PersonMock
{
    private static PersonCollection people =
        new PersonCollection();

    public static PersonCollection GetPeople()
    {
        if (people.Count > 0)
            return people;

       for (int i = 0; i < 10; i++)
           people.Add
               (
                   new Person
                   {
                       FirstName = "FirstName" + i.ToString(),
                       LastName = "LastName" + i.ToString()
                   }
               );

       return people;
   }
}

Nun können wir daran gehen, die ListBox zu stylen:

Dafür ist ein DataTemplate zu erstellen. Dieses wird als ItemTemplate verwendet und beschreibt somit die Darstellung jedes einzelnen Eintrages der ListBox:

<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              DataType = "Person" >
    <Border
        Cursor="Hand"
        Name="BorderControl"
        Background="WhiteSmoke"
        BorderThickness="0"
        CornerRadius="5"
        Padding="5">
        <TextBlock
            Name = "TextControl"
            Grid.Column="0"
            Text="{Binding}"
            Background="Transparent"
            Padding="4">
            <TextBlock.Style>
                <Style TargetType = "TextBlock" >
                    <Style.Triggers >
                        <DataTrigger
                            Binding="{Binding ElementName=BorderControl,Path=IsMouseOver}"
                            Value="True">
                            <Setter Property = "Foreground" Value="White"/>
                            <Setter Property = "FontWeight" Value="Bold"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBlock.Style>
        </TextBlock>
    </Border>
    <DataTemplate.Triggers>
        <EventTrigger
            SourceName="BorderControl"
            RoutedEvent="TextBlock.MouseEnter">
            <BeginStoryboard>
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="BorderControl"
                                    Storyboard.TargetProperty= "Background.Color"
                                    To="DarkRed" Duration= "00:00:00.2" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
        <EventTrigger
            SourceName= "BorderControl"
            RoutedEvent= "TextBlock.MouseLeave" >
            <BeginStoryboard>
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName= "BorderControl"
                                    Storyboard.TargetProperty= "Background.Color"
                                    To= "WhiteSmoke" Duration= "00:00:00.2" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Das DataTemplate selbst ist sehr einfach gestrickt. Es definiert einen Border, der um ein TextBlock-Element gelegt wird. Der wesentlich interessantere Part liegt bei den verwendeten Triggern. Es gibt einen DataTrigger für das TextBlock-Element und einige EventTrigger. Worin liegt der genaue Unterschied? Ein DataTrigger wird an eine Eigenschaft gebunden und überwacht deren Änderungen. Ist der definierte Wert gesetzt, werden die im Trigger definierten Setter ausgeführt. Ändert sich der Wert nochmals, wird der ursprüngliche Zustand hergestellt.

Ein EventTrigger fungiert wie ein Eventhandler für Ereignisse. Das Ereignis wird abonniert und tritt dieses auf, wird die definierte Aktion ausgeführt. In diesem Beispiel wird eine Animation für den Eintrag ausgeführt, der aktuell unterhalb der Maus liegt. Dies wird über die Events MouseEnter und MouseLeave festgestellt. Das erste Ereignis wird geworfen, sobald die Maus in den sichtbaren Bereich eines Elementes gelangt. In diesem Fall wird mit Hilfe einer Animation das Aussehen verändert. Das MouseLeave-Ereignis wird ausgelöst, wenn die Maus den Bereich des Elementes wieder verlässt. In diesem Fall wird durch eine Animation das ursprüngliche Aussehen wieder hergestellt.

Für eine ListBox reicht das aktuell definierte Styling noch nicht. Es fehlen noch zwei Dinge: Erstens erscheint bei einem Klick auf ein Element ein Fokusrand, der beseitigt werden soll. Dies führt und schlussendlich dazu, dass ein ausgewähltes Item noch nicht gestyled wurde. Sehen wir uns hierzu den entsprechenden Style an:

<Style xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       x:Key="GlowContainer" TargetType="{x:Type ListBoxItem}">

    <Setter Property="FocusVisualStyle">
        <Setter.Value>
            <Style>
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border
                                Background="Transparent"
                                Opacity="0"
                                CornerRadius="0">
                                <Rectangle
                                    Margin="0"
                                    StrokeThickness="0"
                                    Stroke="Transparent"
                                    StrokeDashArray="1 2"/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Border Background="WhiteSmoke"
                        CornerRadius="5"
                        BorderThickness="2"
                        x:Name="ItemBorder"
                        Margin="6,3,6,3" >
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="true">
                        <Setter
                            TargetName="ItemBorder"
                            Property="BitmapEffect">
                            <Setter.Value>
                                <OuterGlowBitmapEffect
                                    GlowColor="DarkRed"
                                    GlowSize="5" />
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Der definierte Style enthält zwei Setter. Im ersten wird der ItemContainerStyle definiert. Der Item-Container einer ListBox wird durch ein ListBoxItem dargestellt. Erhält dieses den Fokus, wird dieser dargestellt. In unserem Style definieren wir nun einen Border, der transparent dargestellt wird. Dadurch ist der Fokus nicht mehr zu sehen.

Damit das ausgewählte Element entsprechend dargestellt wird, werden im zweiten Setter die notwendigen Vorkehrungen getroffen. So wird ein ControlTemplate definiert. Dieses besteht aus einem Border und einem ContentPresenter. Den Border werden wir nutzen, um einen Glow-Effekt darzustellen. Der ContentPresenter stellt den Platzhalter für den anzuzeigenden Inhalt dar.

Zusätzlich enthält das ControlTemplate einen Trigger. Dieser hängt sich an die Eigenschaft IsSelected und schlägt an, sobald der Wert auf true gestellt wird, also das Element ausgewählt wurde. In diesem Fall wird ein Outer Glow definiert und die Eigenschaft BitmapEffect gesetzt. Damit wäre das ausgewählte Element auch gestyled.

Wer an diesem Beispiel testen möchte, kann dieses unterhalb herunter laden. Ich empfehle jedoch einen genaueren Blick auf Control Templates, Data Templates, Triggers, Effekte und Animationen, um eine tieferes Verständnis zu erhalten.

Download

Veröffentlicht von Norbert Eder

Ich bin ein leidenschaftlicher Softwareentwickler. Mein Wissen und meine Gedanken teile ich nicht nur hier im Blog, sondern auch in Fachartikeln und Büchern.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Cookie-Einstellungen
Auf dieser Website werden Cookie verwendet. Diese werden für den Betrieb der Website benötigt oder helfen uns dabei, die Website zu verbessern.
Alle Cookies zulassen
Auswahl speichern
Individuelle Einstellungen
Individuelle Einstellungen
Dies ist eine Übersicht aller Cookies, die auf der Website verwendet werden. Sie haben die Möglichkeit, individuelle Cookie-Einstellungen vorzunehmen. Geben Sie einzelnen Cookies oder ganzen Gruppen Ihre Einwilligung. Essentielle Cookies lassen sich nicht deaktivieren.
Speichern
Abbrechen
Essenziell (1)
Essenzielle Cookies werden für die grundlegende Funktionalität der Website benötigt.
Cookies anzeigen