Seminarverwaltung IVb: Kunden und Seminare

In diesem Teil der Artikelreihe zur Seminarverwaltung zeigen wir, wie Sie die Seminare verwalten. Dazu benötigen wir zwei Seiten – eine zur Anzeige der Übersicht aller Seminare und eine zur Anzeige der Details eines einzelnen Seminars beziehungsweise zum Anlegen eines neuen Seminars. Dabei müssen wir noch eine kleine Anpassung am Entity Date Model durchführen, und außerdem legen wir noch die Methoden im Hauptfenster an, mit denen Sie die beiden Seiten zur Anzeige und Bearbeitung der Seminare darstellen

Wir haben diesen Artikel gegenüber der ursprünglichen Planung dreigeteilt. Den zweiten Teil finden Sie hier vor. Im ersten Teil namens Seminarverwaltung IVa: Kunden und Seminare (www.datenbankentwickler.net/273) stellen wir die beiden Seiten zur Anzeige der Übersicht und der Details der Kunden vor.

Im dritten Teil zeigen wir unter dem Titel Seminarverwaltung IVc: Kunden und Seminare (www.datenbankentwickler.net/295), wie Sie den Kunden Seminare zuordnen und umgekehrt.

Im aktuellen Teil der Artikelreihe schauen wir uns die Seiten zur Darstellung der Seminare in der Übersicht und in der Einzelansicht an. Die hier beschriebenen Seiten lauten:

  • SeminareUebersicht.xaml: Die Übersichtsseite für die Seminare soll eine Liste aller Seminare anzeigen. Hier soll der Benutzer eine Suchfunktion vorfinden sowie Schaltflächen zum Anlegen neuer Seminare und zum Bearbeiten oder Löschen von Seminaren.
  • Seminardetails.xaml: Die Detailseite eines Seminars zeigt neben den Details des Seminars auch eine Liste der Kunden an, die dieses Seminar gebucht haben. Sie können hier manuell Kunden hinzufügen oder aus der Liste der Teilnehmer entfernen. Außerdem finden Sie hier Funktionen, um den Kunden Informationen zum Seminar zuzusenden – beispielsweise E-Mails mit dem Link zur Teams-Sitzung, in der das Seminar stattfindet, oder für die Aufzeichnung des Seminars.

Änderungen am Entity Data Model

Die Entitäten für die Seminare enthalten ein Datumsfeld. Damit dieses beim Speichern in der Datenbank korrekt verarbeitet werden kann, fügen wir der Methode OnModelCreating eine Anweisung hinzu, welche den Datentyp für die Eigenschaft Seminartermin auf datetime2 festlegt:

Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
     MyBase.OnModelCreating(modelBuilder)
     ...
     modelBuilder.Entity(Of Seminar).        Property(Function(s) s.Seminartermin).HasColumnType("datetime2").HasPrecision(0)
End Sub

Aufruf der Seminarübersicht über das Ribbon

Im Artikel Seminarverwaltung II: Ribbon und Frame (www.datenbankentwickler.net/272) haben wir bereits den Aufbau des Ribbons für diese Anwendung beschrieben. Was noch fehlt, sind die Ereignismethoden, die durch die Schaltflächen btnSeminaruebersicht und btnSeminaranlegen ausgelöst werden. Mit diesen Schaltflächen wollen wir die noch zu erstellenden Seiten SeminareUebersicht und Seminardetails aufrufen. Die Methode, die durch die Schaltfläche btnSeminaruebersicht ausgelöst werden soll, erstellt eine neue Seite auf Basis des Page-Elements SeminareUebersicht und übergibt dieser einen Verweis auf das Frame-Objekt und auf den Datenbankkontext aus dbContext. Dann weist sie der Eigenschaft Content des Frame-Elements das neue Page-Element zu:

Private Sub btnSeminaruebersicht_Click(sender As Object, e As RoutedEventArgs)
     Dim pgeSeminaruebersicht As New SeminareUebersicht(fra, dbContext)
     fra.Content = pgeSeminaruebersicht
End Sub

Aufruf der Seite zum Anlegen eines neuen Seminars über das Ribbon

Ähnlich sieht der Aufruf für die Schaltfläche btnSeminarAnlegen aus:

Private Sub btnSeminarAnlegen_Click(sender As Object, e As RoutedEventArgs)
     Dim pgeSeminardetails As New Seminardetails(fra, dbContext)
     fra.Content = pgeSeminardetails
End Sub

Validierung der Seminare bei der Eingabe

Bevor wir die Benutzeroberfläche und die Anwendungslogik für das Anlegen neuer Seminare und das Bearbeiten vorhandener Seminare kreieren, benötigen wir noch eine Klasse, welche die Validierungsregeln für die Seminar-Elemente enthält. Dafür legen wir eine Klasse namens Seminar.vb an, in der wir die Schnittstelle IDataErrorInfo implementieren. Diese enthält die beiden Eigenschaften Item und Error. Für die Eigenschaft Item hinterlegen wir die Validierungsregeln, die Eigenschaft Error brauchen wir nicht anzupassen.

Die beiden Felder Titel und Inhalt dürfen einfach nicht leer sein, was wir durch die Prüfung des Inhalts dieser Felder mit String.IsNullOrEmpty prüfen. Der Inhalt des Feldes Preis darf nicht leer sein und weder einen negativen noch einen nicht-numerischen Wert enthalten. Das Feld Seminartermin darf ebenfalls nicht leer sein und muss ein Datum enthalten:

Imports System.ComponentModel
Public Class Seminar
     Implements IDataErrorInfo
     Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
         Get
             Dim strErrorMessage As String = ""
             Select Case columnName
                 Case "Titel"
                     If (String.IsNullOrEmpty(Titel)) Then
                         strErrorMessage = "Bitte geben Sie einen Titel ein."
                     End If
                 Case "Inhalt"
                     If (String.IsNullOrEmpty(Inhalt)) Then
                         strErrorMessage = "Bitte geben Sie einen Inhalt ein."
                     End If
                 Case "Preis"
                     If (Preis < 0 Or String.IsNullOrEmpty(Preis)) Then
                         strErrorMessage = "Bitte geben Sie einen Preis ein."
                     End If
                 Case "Seminartermin"
                     If (String.IsNullOrEmpty(Seminartermin) Or Not IsDate(Seminartermin)) Then
                         strErrorMessage = "Bitte geben Sie einen Seminartermin ein."
                     End If
             End Select
             Return strErrorMessage
         End Get
     End Property
     Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
         Get
             ''Throw New NotImplementedException()
         End Get
     End Property
End Class

Detailseite zum Anlegen und Bearbeiten von Seminaren

Damit gehen wir gleich über zu der Seite, mit der wir neue Seminare anlegen und vorhandene Seminare bearbeiten wollen. Im Teil mit den Resources für das Page-Element definieren wir wieder einige allgemeine Eigenschaften für die Steuerelemente wie Margin und Padding für die Elemente DatePicker und Button. Für das Element TextBox legen wir außerdem noch einen Wert für das Attribut Validation.ErrorTemplate fest, mit dem wir das Aussehen der TextBox-Elemente für den Fall definieren, dass der Inhalt nicht erfolgreich validiert werden konnte. Diese Teile finden Sie im Beispielprojekt, wir haben sie an dieser Stelle aus Platzgründen ausgespart:

<Page x:Class="Seminardetails"... Title="Seminardetails">
     <Page.Resources>
         ...

Ein weiteres Style-Element definiert die Regeln, nach denen Button-Elemente, die diese Regel aufnehmen, deaktiviert werden sollen. Diese Regeln richten sich nach der Validierung, genauer danach, ob die Validierung für ein bestimmtes Steuerelement fehlgeschlagen ist.

     <Style x:Key="EnableOnValidation" TargetType="{x:Type Button}">
         <Style.Triggers>
             <DataTrigger Binding="{Binding ElementName=txtTitel, Path=(Validation.HasError)}" Value="True">
                 <Setter Property="IsEnabled" Value="False"></Setter>
             </DataTrigger>
             <DataTrigger Binding="{Binding ElementName=txtInhalt, Path=(Validation.HasError)}" Value="True">
                 <Setter Property="IsEnabled" Value="False"></Setter>
             </DataTrigger>
             <DataTrigger Binding="{Binding ElementName=txtPreis, Path=(Validation.HasError)}" Value="True">
                 <Setter Property="IsEnabled" Value="False"></Setter>
             </DataTrigger>
             <DataTrigger Binding="{Binding ElementName=txtSeminartermin, Path=(Validation.HasError)}"                     Value="True">
                 <Setter Property="IsEnabled" Value="False"></Setter>
             </DataTrigger>
         </Style.Triggers>
     </Style>
</Page.Resources>

Anschließend folgt die Definition für die RowDefinition– und die ColumnDefinition-Elemente für das Grid dieses Page-Elements, die wir hier ebenfalls nicht abbilden.

Steuerelemente der Seite Seminardetails.xaml

Interessanter wird es wieder bei der Definition der Steuerelemente der Seite Seminardetails.xaml, die wie in Bild 1 aussehen sollen. Hier finden wir zwei Spalten, von denen die erste jeweils das Label-Element aufnimmt und die zweite die eigentlichen gebundenen Steuerelemente.

Entwurf der Seite zur Anzeige der Seminardetails

Bild 1: Entwurf der Seite zur Anzeige der Seminardetails

Damit die Steuerelemente die Werte des jeweiligen Datensatzes beziehungsweise des jeweiligen Elements des Entity Data Models anzeigen, werden wir im Anschluss das Code behind-Modul mit einer Eigenschaft namens AktuellesSeminar füllen, welches wir beim Anlegen eines neuen Seminars oder beim Bearbeiten eines vorhandenen Seminars jeweils mit dem entsprechenden Element füllen.

Für die zu bindende Eigenschaft geben wir daher einen Ausdruck wie {Binding -AktuellesSeminar.ID, Mode=TwoWay} an – hier für das Binden an das Feld ID. Für die Elemente mit Validierung definieren wir zusätzlich noch das Attribut ValidatesOnDataErrors wie in {Binding AktuellesSeminar.Titel, Mode=TwoWay, ValidatesOnDataErrors=True}.

Die ID bilden wir in einem TextBox-Element ab, das wir mit dem Wert False für das Attribut IsEnabled für die Eingabe deaktivieren. Die Felder Titel und Inhalt werden jeweils mit TextBox-Elementen dargestellt, die über das Attribut Text an diese Felder gebunden sind.

Für das Feld Preis legen wir noch zur Anzeige des Wertes als Währung noch ein spezielles Format fest, nämlich StringFormat: {Binding -AktuellesSeminar.Preis, Mode=TwoWay, ValidatesOnDataErrors=True, StringFormat=C}.

Das Datum zeigen wir mit einem DatePicker-Steuerelement an, die im Feld Seminartermin enthaltene Uhrzeit liefern wir mit einer separaten TextBox, die wir ebenfalls an das Feld Seminartermin binden. Allerdings legen wir hier mit StringFormat=”t” fest, dass nur die Uhrzeit angezeigt wird.

Die gebundenen Felder definieren wir insgesamt wie folgt:

     <Grid>
         ...
         <Label Content="ID:" Grid.Column="0" />
         <TextBox x:Name="txtID" Grid.Column="1" HorizontalAlignment="Left" Text="{Binding -AktuellesSeminar.ID, Mode=TwoWay}" Width="50" IsEnabled="False" BorderBrush="Transparent" /> 
         <Label Content="Titel:" Grid.Column="0" Grid.Row="1" />
         <TextBox x:Name="txtTitel" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" -Text="{Binding AktuellesSeminar.Titel, Mode=TwoWay, ValidatesOnDataErrors=True}" />
         <Label Content="Inhalt:" Grid.Row="2" />
         <TextBox x:Name="txtInhalt" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Stretch" -Text="{Binding AktuellesSeminar.Inhalt, Mode=TwoWay, ValidatesOnDataErrors=True}" />
         <Label Content="Preis:" Grid.Row="3" />
         <TextBox x:Name="txtPreis" Grid.Column="1" Grid.Row="3" Width="100" Text="{Binding -AktuellesSeminar.Preis, Mode=TwoWay, ValidatesOnDataErrors=True, StringFormat=C}" />
         <Label Content="Seminartermin:" Grid.Row="4" />
         <DatePicker x:Name="dpSeminartermin" Grid.Column="1" Grid.Row="4" Width="140" -HorizontalAlignment="left" SelectedDate="{Binding AktuellesSeminar.Seminartermin, Mode=TwoWay, ValidatesOnDataErrors=True, TargetNullValue=''''}"></DatePicker>
        <Label Content="Uhrzeit:" Grid.Row="5" />
         <TextBox x:Name="txtUhrzeit" Grid.Column="1" Grid.Row="5" Width="140" HorizontalAlignment="left" Text="{Binding AktuellesSeminar.Seminartermin, Mode=TwoWay, ValidatesOnDataErrors=True, TargetNullValue='''', StringFormat=''t''}"></TextBox>

Schaltflächen zum Speichern und zum Verwerfen eines Seminars

Damit fehlen noch die beiden Schaltflächen, die wir in einem StackPanel-Element nebeneinander anordnen. Die Schaltfläche btnSpeichern soll nur aktiviert werden, wenn die in der weiter oben definierten statischen Ressource EnableOnValidation gestellten Bedingungen erfüllt sind.

Deshalb legen wir im Style-Element der Schaltfläche mit dem BasedOn fest, das die dortigen Bedingungen für den Wert des Attributs Enabled geprüft werden sollen. Die beiden Schaltflächen enthalten wiederum jeweils ein StackPanel-Element, welches nebeneinander das Icon und den Schaltflächentext beinhaltet:

         <StackPanel Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="4" >
             <Button x:Name="btnSpeichern" Click="btnSpeichern_Click">
                 <Button.Style>
                     <Style TargetType="{x:Type Button}" BasedOn="{StaticResource EnableOnValidation}">
                         <Style.Setters>
                             <Setter Property="Margin" Value="2"></Setter>
                         </Style.Setters>
                     </Style>
                 </Button.Style>
                 <StackPanel Orientation="Horizontal">
                     <Image Source="images/ok.png" Width="24" Height="24"></Image>
                     <Label>OK</Label>
                 </StackPanel>
             </Button>
             <Button x:Name="btnVerwerfen" Click="btnVerwerfen_Click">
                 <StackPanel Orientation="Horizontal">
                     <Image Source="images/delete.png" Height="24" Width="24"></Image>
                     <Label>Verwerfen</Label>
                 </StackPanel>
             </Button>
         </StackPanel>
     </Grid>
</Page>

Anwendungslogik in der Datei Seminarverwaltung.xaml.vb

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