Das auf .Net basierende Cross-Plattform UI Framework Avalonia verfügt über eine große Palette an Standard-Controls. Es ist vieles dabei, was man als Entwickler typischerweise braucht. So gibt es etwa diverse Eingabeboxen, Controls für Auflistungen und welche, über die das Layout beeinflusst werden kann. Wenn ich auf Business-Applikationen schaue, fällt mir zudem noch ein sehr wichtiges ein, welches Avalonia mitbringt: Das DataGrid. Die Aufgabe eines DataGrids besteht grundsätzlich darin, eine mehr oder weniger lange Liste von Objekten in tabellarischer Form darzustellen. Jede Zeile ist ein solches Objekt, jede Spalte bezieht sich auf eine Eigenschaft. Daneben gibt es typischerweise Anforderungen wie zum Beispiel Sortierung, Filterung oder Gruppierung. Das DataGrid in Avalonia liefert diese Features entweder direkt oder bringt sie über den kleinen Umweg einer CollectionView im ViewModel mit. In diesem Artikel möchte ich mich mit den Features beschäftigen, welche das DataGrid in Avalonia im Standard mitbringt und wie man diese verwendet.
Inhaltsverzeichnis
Das Beispielprogramm
Anhand eines kleinen Beispielprogramms möchte ich durch verschiedene Funktionen des DataGrids von Avalonia führen. Das Coding dazu ist unter [1] zu finden. Das Programm ist an sich recht einfach. Es gibt eine längere Liste an Testdaten, welche mit dem Generator unter [2] erzeugt wurden. Die Testdaten sind generierte Benutzerdaten mit Eigenschaften wie Name, Postleitzahl, Adresse oder Land. Das DataGrid zeigt diese Testdaten an und lässt alle Felder editieren. Weiterhin ermöglicht das PropertyGrid auf der rechten Seite das Editieren der gerade ausgewählten Zeile. Das PropertyGrid kommt nicht aus dem Standard von Avalonia, sondern ist eine Eigenentwicklung wie unter [3] beschrieben. Nachfolgend ein Screenshot aus dem Beispielgrogramm.
Die ersten Schritte
Zunächst muss man wissen, dass das DataGrid nicht wie die allermeisten anderen Controls im Nuget-Paket Avalonia [4] enthalten ist. Es wird über ein zusätzliches Paket namens Avalonia.Controls.DataGrid (Siehe [5]) ins Projekt eingebunden. Anschließend darf man nicht vergessen, die notwendigen Styles per StyleInclude in das Programm holen. Das DataGrid zeigt ohne dieses StyleInclude nichts an. Nachfolgender Codeausschnitt zeigt das StyleInclude in meinem Beispiel innerhalb der App.xaml Datei. Hier verwende ich einen FluentTheme und muss entsprechend die Datei Fluent.xaml des DataGrid einbinden – Light oder Dark ist für das DataGrid egal. Das StyleInclude sieht entsprechend anders aus, wenn anstelle des FluentTheme der ältere DefaultTheme verwendet wird. Letztere Variante habe ich im Codebeispiel als Kommentar mit angefügt.
<Application xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="HappyCoding.AvaloniaAppWithDataGrid.App"> <Application.Styles> <FluentTheme Mode="Light" /> <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" /> <!-- Older alternative to FluentTheme <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml" /> <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml" /> <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml" /> --> </Application.Styles> </Application>
Anschließend kann man das DataGrid verwenden (siehe nachfolgender Codeausschnitt). Im Beispiel benutze ich als Datenquelle eine ObservableCollection im ViewModel. Diese wird an die Items-Eigenschaft des DataGrid gebunden. Ebenso verknüpft ein Binding die gerade ausgewählte Zeile mit dem ViewModel. Als SelectionMode verwende ich hier Single. Avalonia bietet daneben ein Multiple für den SelectionMode an, um dem Benutzer mehrere Zeilen gleichzeitig auswählen zu lassen. In meinem Beispiel habe ich AutoGenerateColums deaktiviert und gebe die anzuzeigenden Spalten manuell an. Im Beispiel ist das wohl Geschmackssache, ich selbst lege Spalten i. d. R. explizit an. Standardmäßig ist diese Eigenschaft auf true, wodurch sich das DataGrid selbst um das Anlegen der Spalten kümmert. Die Eigenschaften CanUserResizeColumns, CanUserReorderColumns und CanUserSortColumns steuern die jeweiligen Features.
<DataGrid Items="{Binding Path=Items}" SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" SelectionMode="Single" AutoGenerateColumns="False" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True"> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" /> <DataGridTextColumn Header="PostalZip" Binding="{Binding Path=PostalZip}" /> <!-- ... --> </DataGrid>
Customizing der Spalten durch den Benutzer
Im Beispiel lassen wir dem Benutzer die Größe der Spalten und deren Reihenfolge ändern. Beides ist entsprechend intuitiv abgebildet. Die Größe der Spalten ändert sich durch das Ziehen am Spaltenrand. Die Reihenfolge der Spalten kann per Drag-Drop angepasst werden.
Sortierung funktioniert out-of-the-box
Der Benutzer kann Spalten quasi out-of-the-box sortieren. Hierzu reicht es, die Eigenschaft CanUserSortColums am DataGrid auf true zu setzen (Default ist true). Die Sortierung wird aktiv, sobald der Benutzer auf eine Spalte klickt. Die Sortierreihenfolge ändert sich, sobald der Benutzer nochmal auf die gleiche Spalte klickt. Mit gedrückter Shift-Taste kann der Benutzer auch nach mehreren Spalten gleichzeitig sortieren. Mit gedrückter Strg-Taste lässt sich die Sortierung wieder zurücksetzen. Im nachfolgenden Screenshot ist nach den Spalten Country und Region sortiert. Die an einer Spalte aktive Sortierung wird über einen Pfeil dargestellt.
Editieren der Daten im DataGrid
Der Benutzer kann die Zellen im DataGrid direkt bearbeiten (sofern nicht ReadOnly). Auf technischer Seite erfolgt das Editieren über ein normales Eingabe-Control, etwa eine TextBox. Dieses wird in der Zelle aktiv, welche gerade bearbeitet wird. Zellen reagieren ebenso auf Änderungen aus der Datenquelle per INotifyPropertyChanged.
Templated Columns
Der Standard bietet drei Arten von Spalten: DataGridTextColumn, DataGridCheckBoxColumn und DataGridTemplateColumn. Das ist nicht viel. Die DataGridTemplateColumn ist dafür sehr flexibel. Sie erlaubt es, beliebige Controls für die Anzeige und für das Editieren einzubinden. Im nachfolgenden Codeausschnitt etwa verwenden wir eine DataGridTemplateColumn, um ein Geburtsdatum anzuzeigen und ändern lassen zu können.
<DataGridTemplateColumn Header="BirthDate" SortMemberPath="BirthDate" Width="150"> <DataGridTemplateColumn.CellEditingTemplate> <DataTemplate> <CalendarDatePicker SelectedDate="{Binding Path=BirthDate}" /> </DataTemplate> </DataGridTemplateColumn.CellEditingTemplate> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Path=BirthDate, StringFormat={}{0:dd-MM-yyyy}}"></TextBlock> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>
Die Eigenschaft SortMemberPath zeigt auf die Eigenschaft, nach der in dieser Spalte sortiert werden kann. Die beiden Templates CellEditingTemplate und CellTemplate sind jeweils für das Bearbeiten und für die Anzeige der Daten im DataGrid. Im CellTemplate verwende ich lediglich einen TextBlock, welcher das Datum formatiert. Im CellEditingTemplate verwende ich stattdessen einen CalendarDatePicker, bei dem der Benutzer das Datum mithilfe eines Kalenders ändern kann.
Gruppieren und Filtern
Zuletzt möchte ich auf die Funktionen Gruppieren und Filtern eingehen. Nicht das DataGrid selbst, sondern die Klasse DataGridCollectionView stellt diese Funktionen bereit. Sie kann im ViewModel anstelle der obigen ObservableCollection als Datenquelle verwendet werden. An der DataGridCollectionView lassen sich schließlich Gruppierungskriterien und Filter einstellen. Nachfolgender Codeausschnitt zeigt dazu ein einfaches ViewModel.
public class WithGroupAndFilterViewModel : ViewModelBase { private TestDataRow? _selectedItem; private DataGridCollectionView Items { get; } public TestDataRow? SelectedItem { get => _selectedItem; set => SetField(ref _selectedItem, value); } public WithGroupAndFilterViewModel() { var testData = TestDataLoader.LoadTestData(100, 500); this.Items = new DataGridCollectionView(testData, false, false); this.Items.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(TestDataRow.Country))); this.Items.Filter = x => ((TestDataRow) x).Status == true; this.SelectedItem = testData.FirstOrDefault(); } }
In diesem Beispiel wird nach der Eigenschaft Country gruppiert. Der Filter ist technisch als Lambda-Funktion umgesetzt. Diese filtert alle Zeilen mit Status gleich false raus. Innerhalb des Xaml-Codes ändert sich im Vergleich zum letzten Beispiel nichts. Das DataGrid übernimmt die Einstellungen aus der DataGridCollectionView direkt. Nachfolgender Screenshot zeigt das Ergebnis.
Fazit
In diesem Artikel haben wir uns mit vielen Funktionen des DataGrid in Avalonia beschäftigt. Allgemein lässt sich sagen, es ist vieles von dem da, was man als Entwickler typischerweise braucht. Das Schwierige ist nur hin und wieder die Dokumentation. Einige der Features, insbesondere die zuletzt vorgestellte Gruppierung, sind in der Dokumentation schwierig oder gar nicht zu finden. Hier hilft es häufig, das Repository von Avalonia unter [6] durchzuschauen.
Downloads
- Quellcode Beispielprogramm
https://www.rolandk.de/files/2022/HappyCoding.AvaloniaAppWithDataGrid.zip
Verweise
- Quellcode Beispielprogramm auf GitHub
https://github.com/RolandKoenig/HappyCoding/tree/main/2022/HappyCoding.AvaloniaAppWithDataGrid - Tool zum Generieren der Testdaten
https://generatedata.com/ - PropertyGrid auf Basis von Avalonia
https://www.rolandk.de/wp-posts/2020/08/propertygrid-mit-avalonia/ - Nuget-Paket von Avalonia
https://www.nuget.org/packages/Avalonia/ - Nuget-Paket des DataGrid
https://www.nuget.org/packages/Avalonia.Controls.DataGrid - Repository von Avalonia auf GitHub
https://github.com/AvaloniaUI/Avalonia
Ebenfalls interessant
- Allgemeiner Artikel zu Avalonia als Cross-Plattform-UI-Framework
https://www.rolandk.de/wp-posts/2020/07/cross-platform-gui-mit-c-und-avalonia/ - Custom Window Chrome mit Avalonia
https://www.rolandk.de/wp-posts/2021/05/custom-window-chrome-mit-avalonia/ - Markdown-Dokumente mit Avalonia rendern
https://www.rolandk.de/wp-posts/2021/08/markdown-dokumente-mit-avalonia-rendern/