EDM: Bestellungen und Bestellpositionen

Wenn Sie ein Abonnement des Magazins 'DATENBANKENTWICKLER' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

EDM: Bestellungen und Bestellpositionen

Auch in dieser Ausgabe geht es mit unserer Beispiellösung Bestellverwaltung weiter. Nachdem wir bereits Kunden, Produkte und Kategorien verwalten können, nehmen wir uns nun die Bestellungen und Bestellpositionen vor. Dabei brauchen wir ein paar neue Seiten, um die Details einer Bestellung mit den Bestellpositionen und die Details einer neuen oder zu bearbeitenden Bestellposition anzuzeigen. Damit werden wir erstmal die Bearbeitung von Daten einer m:n-Beziehung ermöglichen.

Neue Funktionen

Die neue Version der Bestellverwaltung enthält wieder eine neue Gruppe im Ribbon, mit der zwei neue Seiten geöffnet werden können. Die erste davon ist eine Übersicht der Bestellungen in absteigender Reihenfolge nach dem Bestelldatum (siehe Bild 1).

Übersicht der Bestellungen

Bild 1: Übersicht der Bestellungen

Hier kann der Benutzer die aktuell markierte Bestellung per Mausklick löschen oder auch eine neue Bestellung anlegen. Per Doppelklick lässt sich die angeklickte Bestellung öffnen (siehe Bild 2).

Bestellung in der Detailansicht

Bild 2: Bestellung in der Detailansicht

Ein Klick auf den Ribbon-Eintrag Neue Bestellung soll den Dialog mit den Bestelldetails für eine neue Bestellung anzeigen, wobei direkt das Kombinationsfeld zur Auswahl des Kunden für diese Bestellung ausgeklappt wird (siehe Bild 3).

Anlegen einer neuen Bestellung

Bild 3: Anlegen einer neuen Bestellung

Auch wenn der Benutzer mit einem Klick auf die Schaltfläche Neue Bestellposition klickt, soll sich direkt das Kombinationsfeld zur Auswahl des betroffenen Produkts öffnen und die schnelle Auswahl erleichtern.

Wenn der Benutzer dann das gewünschte Produkte ausgewählt hat, sollen direkt die Werte der Felder Einzelpreis, Mehrwertsteuersatz und Gewicht aus der Tabelle Produkte in den neuen Datensatz der Tabelle tblBestellpositionen übernommen werden.

Die Detailseite für eine Bestellposition, wie sie nach einem Doppelklick auf einen der Einträge der Seite mit den Bestelldetails aussieht, finden Sie in Bild 4.

Bearbeiten einer Bestellposition

Bild 4: Bearbeiten einer Bestellposition

Datenbankerweiterungen

Für diese neue Version der Bestellverwaltung haben wir ein paar kleine Änderungen am Datenmodell der SQLite-Datenbank Bestellungen.db vorgenommen. Dabei sind wie auf zwei Probleme gestoßen, die wir wie folgt gelöst haben:

  • Mit dem DBBrowser for SQLite konnten wir in manchen Fällen keine Zahlenfelder zu einer bestehenden Tabelle hinzufügen. Dies führt im schlimmsten Fall zum Leeren der kompletten Datenbank. Wir sind dann zu einem anderen Tool namens SQLiteStudio gewechselt (sqlitestudio.pl), welches die Änderungen klaglos vorgenommen hat.
  • Als wir die Änderungen an der Datenbank in das Entity Data Model übernehmen wollten, gab es ebenfalls Probleme. Die Änderungen übernehmen Sie, indem Sie das Entity Data Model (Datei BestellverwaltungEntities.edmx) öffnen und dann den Kontextmenüeintrag Modell aus der Datenbank aktualisieren ... auswählen). Die nachfolgende Fehlermeldung, deren Text mit [A]System.Data.SQLite.SQLiteConnection cannot be cast to [B]System.Data.SQLite.SQLiteConnection. begann, wurde behoben, nachdem wir die aktuelle Version des NuGet-Pakets System.Data.SQLite sowie die aktuelle Runtime-Komponente von der Internetseite https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki installiert und Visual Studio dann neu gestartet haben.

Hinzufügen der neuen Ribbon-Elemente

Dieser Teil bringt keine neuen Techniken. Die beiden neu hinzugefügten Ribbon-Schaltflächen heißen btnNeueBestellung und btnBestelluebersicht und laden die Seiten BestellungDetails.xaml beziehungsweise BestellungUebersicht.xaml in das Frame-Element namens Workzone.

Die Schaltfläche btnBestelluebersicht löst die folgende Ereignismethode aus:

private void btnBestelluebersicht_Click(object sender, RoutedEventArgs e) {
     ShowBestellungenUebersicht();
}

Die hier aufgerufene Methode ShowBestellungenUebersicht verwendet den optinalen Parameter bestellungID, der beim Aufruf nach dem Ändern oder Neuanlegen einer Bestellung übergeben werden kann, damit der geänderte oder neu angelegte Eintrag im DataGrid markiert werden kann.

Wurde die Seite BestellungUebersicht noch nicht erstellt oder gab es eine Änderung bei einer der Bestellungen, wird die Seite neu erstellt und die Variable BestellungChanged auf false eingestellt. Anschließend abonniert die Klasse MainWindow.xaml.cs die beiden Ereignisse ItemDoubleClicked und NewItem der Klasse BestellungUebersicht.xaml.cs. Schließlich wird die Seite, egal ob neu erzeugt oder bereits vorhanden, im Frame-Steuerelement Workzone angezeigt:

private void ShowBestellungenUebersicht(long bestellungID = 0) {
     if (bestellungUebersicht == null | BestellungChanged == true) {
         bestellungUebersicht = new BestellungUebersicht(bestellungID);
         BestellungChanged = false;
     }
     bestellungUebersicht.ItemDoubleClicked += new BestellungUebersicht.EventHandlerItemDoubleClicked(OnBestellungDoubleClicked);
     bestellungUebersicht.NewItem += new BestellungUebersicht.EventHandlerNewItem(OnNewBestellung);
     WorkZone.Content = bestellungUebersicht;
}

Übersicht der Bestellungen

Schauen wir uns erst die Seite BestellungUebersicht.xaml an. Diese enthält unter anderem ein DataGrid mit drei Spalten, die wie folgt definiert werden. Interessant ist hier wieder die in eine Resource des DataGrid-Elements ausgelagerte Ereignisdefinition für das Ereignis MouseDoubleClick, welches die Methode Row_DoubleClick aufrufen soll.

Außerdem haben wir hier, um die beiden Felder Nachname und Vorname der Auflistung Kunden des Bestellung-Objekts in einem MultiBinding-Element zusammengefasst:

<DataGrid x:Name="dgBestellungen" ItemsSource="{Binding Bestellungen}" AutoGenerateColumns="false" CanUserAddRows="False" Grid.Row="0">
     <DataGrid.Columns>
         <DataGridTextColumn Binding="{Binding Path=ID}" Header="ID" />
         <DataGridTextColumn Binding="{Binding Path=Bestelldatum, StringFormat={0:dd.MM.yyyy}}" Header="Bestelldatum" />
         <DataGridTextColumn Header="Kunde Multibinding">
             <DataGridTextColumn.Binding>
                 <MultiBinding StringFormat="{}{0}, {1}">
                     <Binding Path="Kunden.Nachname" />
                     <Binding Path="Kunden.Vorname" />
                 </MultiBinding>
             </DataGridTextColumn.Binding>
         </DataGridTextColumn>
         <DataGridTextColumn Binding="{Binding Path=Kunde}" Header="Kunde" />
     </DataGrid.Columns>
     <DataGrid.Resources>
         <Style TargetType="DataGridRow">
             <EventSetter Event="MouseDoubleClick" Handler="Row_DoubleClick"/>
         </Style>
     </DataGrid.Resources>
</DataGrid>

Das DataGrid wird mit den Bestellungen aus einer ObservableCollection namens bestellungen mit Elementen des Typs Bestellung gefüttert, und zwar über die öffentliche Eigenschaft Bestellungen:

private ObservableCollection<Bestellung> bestellungen;
public ObservableCollection<Bestellung> Bestellungen {
     get { return bestellungen; }
     set { bestellungen = value; }
}

In der Konstruktor-Methode BestellungUebersicht erstellen wir das dbContext-Element und füllen die Collection bestellungen:

public BestellungUebersicht(long bestellungID = 0) {
     InitializeComponent();
     dbContext = new BestellverwaltungEntities();
     bestellungen = new ObservableCollection<Bestellung>(dbContext.Bestellungen.OrderByDescending(c => c.Bestelldatum));
     DataContext = this;
     if (bestellungID != 0) {
         Bestellung currentBestellung = dbContext.Bestellungen.Find(bestellungID);
         dgBestellungen.Focus();
         dgBestellungen.SelectedItem = currentBestellung;
         dgBestellungen.ScrollIntoView(currentBestellung);
     }
}

Neue Bestellung anlegen

Ein Klick auf die Schaltfläche btnNeu der Seite BestellungUebersicht.xaml löst die folgende Methode aus, welche wiederum das Ereignis NewItem aufruft:

private void btnNeu_Click(object sender, RoutedEventArgs e) {
     NewItem(this, null);
}

NewItem ist wie folgt in BestellungUebersicht.xaml.cs definiert:

public delegate void EventHandlerNewItem(object sender, System.EventArgs e);
public event EventHandlerNewItem NewItem;

Das Ereignis NewItem wird im übergeordneten Fenster-Element MainWindow.xaml abonniert, sobald der Benutzer auf die Ribbon-Schaltfläche btnBestelluebersicht klickt (mehr siehe in den Methoden btnBestelluebersicht_Click und ShowBestellungenUebersicht in der Datei MainWindow.xaml.cs). Das Ereignis wird in MainWindow.xaml.cs durch die folgende Ereignisprozedur implementiert:

private void OnNewBestellung(object sender, EventArgs e) {
     BestelldetailsAnzeigen();
}

Damit steigen wir in die Methode BestelldetailsAnzeigen ein, die wir ebenfalls in der Klasse MainWindow.xaml.cs finden. Diese erstellt ein neues Objekt auf Basis der Seite BestelldetailsAnzeigen und übergibt den Parameter bestellungID, der hier 0 ist. Dann abonniert sie vier Ereignisse, nämlich ItemChanged, Cancelled, NewItem und ItemDoubleClicked. Die ersten beiden sind für die beiden Schaltflächen Speichern und Verwerfen, die hinteren beiden für die Schaltflächen Neue Bestellposition und Bestellposition löschen, die zum DataGrid zur Anzeige der Bestellpositionen gehören (siehe Bild 5). Nach dem Abonnieren der Ereignisse weist die Methode das neu erstellte Page-Objekt auf Basis der Klasse BestellungDetails noch der Eigenschaft Content des Frame-Objekts Workzone zu, welches die Seite dann anzeigt:

Entwurf der Bestelldetails samt DataGrid für die Bestellpositionen

Bild 5: Entwurf der Bestelldetails samt DataGrid für die Bestellpositionen

private void BestelldetailsAnzeigen(long bestellungID = 0) {
     bestellungDetails = new BestellungDetails(bestellungID);
     bestellungDetails.ItemChanged += new BestellungDetails.EventHandlerItemChanged(OnBestellungChanged);
     bestellungDetails.Cancelled += new BestellungDetails.EventHandlerCancelled(OnBestellungCancelled);
     bestellungDetails.NewItem += new BestellungDetails.EventHandlerNewItem(OnNewBestellposition);
     bestellungDetails.ItemDoubleClicked += new BestellungDetails.EventHandlerItemDoubleClicked(OnBestellpositionDoubleClicked);
     WorkZone.Content = bestellungDetails;
}

Felder in den Bestelldetails definieren

Die Seite BestellungDetails.xaml besteht aus drei Bereichen: den oberen Steuerelementen zur Eingabe der Bestelldetails, dem DataGrid zur Anzeige der Bestellpositionen und den Schaltflächen zum Hinzufügen und Löschen von Bestellpositionen sowie den beiden Schaltflächen zum Speichern oder Verwerfen des aktuell angezeigten Objekts aus der Tabelle Bestellungen. Schauen wir uns zunächst die drei Steuer­elemente an. Die TextBox mit dem Bezeichnungsfeld ID ist an das Feld Bestellung.ID gebunden. Das heißt, dass wir in der Code behind-Datei ein Objekt namens Bestellung bereitstellen müssen – dazu später mehr. Interessant ist die ComboBox. Hier haben wir nicht wie üblich nur ein einziges Feld als anzuzeigenden Inhalt angegeben, sondern einen aus den beiden Feldern Nachname und Vorname bestehenden Ausdruck, also etwa Minhorst, André. Dies erledigen wir, indem wir das Attribut DisplayMemberPath weglassen und stattdessen ein Unterelement namens ComboBox.DataTemplate einfügen. Dieses soll ein TextBlock-Element enthalten, dessen Eigenschaftselement Text wir mit einem Multibinding der beiden Felder Nachname und Vorname in der mit StringFormat angegebenen Formatierung ausgeben:

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Neues aus unseren Magazinen
Listenfeld: Reihenfolge mehrerer Einträge...

Wir haben bereits in mehreren Beiträgen beschrieben, wie Sie die individuelle Reihenfolge von Elementen einer Tabelle über den Inhalt eines Feldes etwa namens »ReihenfolgeID« einstellen können –... [mehr]

Diagramme mit gefilterten Daten

In Ausgabe 2/2019 haben wir in zwei Artikeln die modernen Diagramme von Access vorgestellt. Im vorliegenen Beitrag zeigen wir Ihnen, wie Sie diese abhängig von den in einem Formular angezeigten... [mehr]

Benutzerverwaltung mit verschlüsselten...

Wenn Sie in einer Access-Anwendung Benutzer verwalten wollen, die sich per Benutzername und Kennwort an die Anwendung anmelden, sollten Sie sehr sensibel mit den in der Anwendung gespeicherten... [mehr]

HTML-Tabellen mit fester Kopfzeile

In den vorherigen Ausgaben von Access im Unternehmen und in der aktuellen Ausgabe arbeiten wir in einigen Beiträgen mit dem Webbrowser-Steuerelement und stellen Daten, die wir mit den Bordmitteln... [mehr]

Flexible HTML-Tabellen mit fester Kopfzeile

Im Beitrag »HTML-Tabellen mit fester Kopfzeile« haben wir gezeigt, wie Sie Daten aus einer bestimmten Abfrage in einem Webbrowser-Steuerelement so anzeigen, dass die Spaltenköpfe oben fixiert... [mehr]

Berechtigungen per HTML verwalten

Im Beitrag »Benutzerverwaltung mit verschlüsselten Kennwörtern« stellen wir eine Lösung vor, in der wir die Berechtigungen von Benutzergruppen an Datenbankobjekten definieren. Dort benötigen wir... [mehr]

Benutzer und Berechtigungen ermitteln

In den Beiträgen »Benutzerverwaltung mit verschlüsselten Kennwörtern« und »Berechtigungen per HTML verwalten« haben wir die Voraussetzungen für eine Benutzerverwaltung geschaffen. Im vorliegenden... [mehr]

Zugriffsrechte mit Datenmakros

Es gibt verschiedene Möglichkeiten, auf Basis des aktuell angemeldeten Benutzers sicherzustellen, dass dieser nur die für ihn vorgesehenen Aktionen mit Daten durchführen darf – beispielsweise durch... [mehr]

Kennwörter generieren

Für den einen oder anderen Zweck möchten Sie vielleicht Kennwörter generieren oder in einer Benutzeroberfläche die Möglichkeit zum Generieren von Kennwörtern anbieten. Wenn Sie etwa Benutzer zu... [mehr]

Neuer Datensatz von Frontend zu Backend

Für manche Themen gibt es keine kurze, prägnante Überschrift. In diesem Fall wollen wir zeigen, wie Sie einen neuen Datensatz anlegen, der in einer temporären Tabelle im Frontend gespeichert wird,... [mehr]