Navigieren mit CollectionViewSource

Bisher haben wir oft mit Detailansichten von Datensätzen wie beispielsweise für Kunden oder Produkten gearbeitet, die zum Anlegen oder Bearbeiten eines einzelnen Datensatzes geeignet waren. Von Access kennen Sie die Möglichkeit, mit den Navigationsschaltflächen auch in solchen Detailformularen zu navigieren und von einem zum anderen Datensatz zu wechseln, ohne zwischendurch zu einem Übersichtsformular zu wechseln. In diesem Artikel wollen wir zeigen, wie Sie das unter WPF so abbilden können, wie es auch unter Access möglich ist. Dabei nutzen wir die Möglichkeiten der CollectionViewSource.

Ziel des Artikels

In diesem Artikel wollen wir einer Seite, die eine Detailansicht eines Datensatzes – hier eines Kunden – anzeigt, Navigationsschaltflächen wie von den Formularen von Access bekannt hinzufügen. Die WPF-Seite besteht dabei grob aus einem Grid-Element, das wiederum zwei weitere Grid-Element aufnimmt. Es definiert außerdem im Element Window.Resources ein CollectionViewSource-Element, das wir im Code behind mit den anzuzeigenden Daten füllen:

<Window x:Class="MainWindow" ... Title="MainWindow" Height="450" Width="800">
     <Window.Resources>
         ...
         <CollectionViewSource x:Key="kundeViewSource"></CollectionViewSource>
     </Window.Resources>

Danach folgt die Definition des übergeordneten Grid-Elements:

     <Grid>
         <Grid.RowDefinitions>
             <RowDefinition Height="Auto"></RowDefinition>
             <RowDefinition Height="Auto"></RowDefinition>
             <RowDefinition Height="*"></RowDefinition>
         </Grid.RowDefinitions>

Das erste untergeordnete Grid-Element erhält als DataContext das als statische Ressource hinterlegte CollectionViewSource-Element sowie einige Zeilen für die Anzeige der einzelnen Felder und Spalten zur Aufteilung von Bezeichnungsfeldern und gebundenen Steuerelementen:

         <Grid DataContext="{StaticResource kundeViewSource}">
             <Grid.RowDefinitions>
                 <RowDefinition Height="Auto"></RowDefinition>
                 ...
                 <RowDefinition Height="*"></RowDefinition>
             </Grid.RowDefinitions>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="Auto"></ColumnDefinition>
                 <ColumnDefinition Width="Auto"></ColumnDefinition>
                 <ColumnDefinition Width="*"></ColumnDefinition>
             </Grid.ColumnDefinitions>

Danach folgen die Definitionen für Steuerelemente, die in den einzelnen Zeilen und Spalten angezeigt werden sollen – zum Beispiel für die ID und den Vornamen:

             <Label Content="ID:" Grid.Column="0" />
             <TextBox x:Name="txtID" Grid.Column="1" HorizontalAlignment="Left"                 Text="{Binding ID, Mode=TwoWay}" Width="50" IsEnabled="False" BorderBrush="Transparent" />
             <Label Content="Vorname:" Grid.Column="0" Grid.Row="2" />
             <TextBox x:Name="txtVorname" Grid.Column="1" Grid.Row="2" Width="202" 
                 Text="{Binding Vorname, Mode=TwoWay, ValidatesOnDataErrors=True}" Margin="2,2,0,3" />
             ...
         </Grid>

Das zweite untergeordnete Grid-Element enthält ein StackPanel-Element mit den vier Schaltflächen und dem Textfeld zur Anzeige der Position des Datensatzzeigers beziehungsweise zur Eingabe des anzuzeigenden Datensatzes, das wir im folgenden Abschnitt beschreiben.

Aufbau der Navigationssteuerelemente

Die Navigationsschaltflächen wollen wir genau wie unter Access abbilden. Das heißt, dass wir von links nach rechts die folgenden Steuerelemente hinzufügen wollen:

  • Schaltfläche zum Navigieren zum ersten Datensatz
  • Schaltfläche zum Navigieren zum vorherigen Datensatz
  • Textfeld zur Anzeige der aktuellen Position im Format x von y, wobei y die Gesamtzahl der angezeigten Datensätze enthält. In dieses Textfeld kann der Benutzer die Position eingeben, zu der er navigieren möchte.
  • Schaltfläche zum Navigieren zum nächsten Datensatz
  • Schaltfläche zum Navigieren zum letzten Datensatz

Die Steuerelemente organisieren wir in einem StackPanel-Steuerelement mit horizontaler Ausrichtung, das wir wie folgt definieren:

<StackPanel Orientation="Horizontal" Grid.Row="10" Grid.ColumnSpan="4" >
     <Button x:Name="btnFirst" Click="btnFirst_Click">
         <Image Source="images/media_beginning.png" Width="24" Height="24"></Image>
     </Button>
     <Button x:Name="btnPrevious" Click="btnPrevious_Click">
         <Image Source="images/media_playback.png" Width="24" Height="24"></Image>
     </Button>
     <TextBox x:Name="txtNavigation" MinWidth="50"         Text="{Binding MyCurrentPosition, UpdateSourceTrigger=LostFocus}"         GotFocus="txtNavigation_GotFocus"></TextBox>
     <Button x:Name="btnNext" Click="btnNext_Click">
         <Image Source="images/media_play.png" Width="24" Height="24"></Image>
     </Button>
     <Button x:Name="btnLast" Click="btnLast_Click">
         <Image Source="images/media_end.png" Width="24" Height="24"></Image>
     </Button>
</StackPanel>

Die Bilddateien für die hier definierten Image-Elemente finden sie im Beispielprojekt im Ordner images. Der XAML-Code hierfür sieht wie folgt aus:

<Grid Grid.Row="1">
     <StackPanel Orientation="Horizontal" Grid.Row="10" Grid.ColumnSpan="4" >
         <Button x:Name="btnFirst" Click="btnFirst_Click" IsEnabled="{Binding FirstOrPreviousEnabled}">
             <Image Source="images/media_beginning.png" Width="24" Height="24"></Image>
         </Button>
         <Button x:Name="btnPrevious" Click="btnPrevious_Click"             IsEnabled="{Binding FirstOrPreviousEnabled, UpdateSourceTrigger=PropertyChanged}">
             <Image Source="images/media_playback.png" Width="24" Height="24"></Image>
         </Button>
         <TextBox x:Name="txtNavigation" MinWidth="50"             Text="{Binding MyCurrentPosition, UpdateSourceTrigger=LostFocus}"             GotFocus="txtNavigation_GotFocus" KeyDown="txtNavigation_KeyDown"></TextBox>
         <Button x:Name="btnNext" Click="btnNext_Click"             IsEnabled="{Binding NextOrLastEnabled, UpdateSourceTrigger=PropertyChanged}">
             <Image Source="images/media_play.png" Width="24" Height="24"></Image>
         </Button>
         <Button x:Name="btnLast" Click="btnLast_Click" 
             IsEnabled="{Binding NextOrLastEnabled, UpdateSourceTrigger=PropertyChanged}">
             <Image Source="images/media_end.png" Width="24" Height="24"></Image>
         </Button>
     </StackPanel>
</Grid>

Für die Schaltflächen finden Sie das Attribut IsEnabled, das wir an die Eigenschaften FirstOrPreviousEnabled beziehungsweise NextOrLastEnabled gebunden haben. Wie diese Eigenschaften definiert sind, beschreiben wir weiter unten.

Der Entwurf des Fensters sieht anschließend wie in Bild 1 aus.

Entwurf des Fensters zur Anzeige der Kunden mit Navigations

Bild 1: Entwurf des Fensters zur Anzeige der Kunden mit Navigations

Anzeige der Daten

Damit überhaupt Daten im Fenster angezeigt werden, verwenden wir im Code behind-Modul eine Variable, mit der wir die CollectionViewSource referenzieren, die wir im XAML-Code als Ressource definiert haben:

Dim kundeViewSource As CollectionViewSource

Diese verbinden wir in der Konstruktor-Methode New mit der Ressource und stellen dann die Eigenschaft Source des CollectionViewSource-Elements auf den Wert der Funktion GetKunden ein:

Public Sub New()
     InitializeComponent()
     DataContext = Me
     kundeViewSource = (Me.FindResource("kundeViewSource"))
     kundeViewSource.Source = GetKunden()
End Sub

Die Funktion GetKunden erstellt eine ObservableCollection mit Elementen auf Basis der Klasse Kunde. Die Definition dieser Klasse sieht gekürzt wie folgt aus:

Partial Public Class Kunde
     Public Property ID As System.Int32
     Public Property Firma As System.String
     Public Property Vorname As System.String
     ...
End Class

Die Funktion GetKunden fügt mit der Add-Methode jeweils ein neues Element hinzu:

Private Function GetKunden() As ObservableCollection(Of Kunde)
     Dim Kunden As New ObservableCollection(Of Kunde)
     Kunden.Add(New Kunde() With {.ID = 1, .Firma = "Krahn GbR", .Vorname = "Adi", _
         .Nachname = "Stratmann", .Anrede = "Herr", .Strasse = "Kremser Straße 54", .PLZ = "10589", _
         .Ort = "Berlin", .Land = "Deutschland", .EMail = "adi@stratmann.de", _
         .Geburtsdatum = "20.02.1939", .Alter = 82, .Newsletter = -1})
     ... ''weitere Add-Aufrufe
     Return Kunden
End Function

Schaltflächen zum Navigieren in den Datensätzen

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins DATENBANKENTWICKLER:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 200 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar