Umstellung DataGrid.ItemsSource von IEnumerable auf DataTable (bzw. DataView)

  • WPF

    Umstellung DataGrid.ItemsSource von IEnumerable auf DataTable (bzw. DataView)

    Ich arbeite zurzeit an einem Projekt, bei dem mehrere Objekte zu einem übergeordneten Objekt gehören (sowas wie "Positionen" in einem "Auftrag"). Die Positionen stammen zwar aus einer Datenbank, sind aber von einer eigenständigen Klasse und bis auf das eigentliche Lesen und Schreiben komplett losgelöst von der Datenbank. Theoretisch könnten sie auch aus einem XML importiert worden sein.
    Einzelne Positionen werden, wenn ausgewählt, in einem PropertyGrid (Windows Forms!) angezeigt. Dabei bediene ich mich der einfachen aber effektiven Nutzung von Attributen zu den Properties der Klasse, um die Anzeige im PropertyGrid steuern zu können. Über das TypeConverter-Attribut beispielsweise kann ich festlegen, dass numerisch gespeicherte Maße in der Form "# mm" angezeigt werden. Oder ich kann mit dem ReadOnly-Attribut festlegen, dass der Nutzer den Wert im PropertyGrid nicht ändern kann. Usw.

    Später wurde dann die Möglichkeit eingebaut, die Liste der Position zusätzlich in einem WPF-DataGrid anzuzeigen. Dazu wird einfach ein Enumerable der Positionen (in diesem Fall eine ObservableCollection<T>) der ItemsSource-Eigenschaft des DataGrids zugewiesen. Leider ist das WPF-DataGrid nicht ganz so automatisch wie das Forms-PropertyGrid, d.h. die den Properties zugeordneten Attribute werden nicht automatisch ausgewertet, so dass ich im AutoGeneratingColumn-Ereignis die PropertyDeskriptoren der Spalten manuell per Code auswerte und die Spalteneigenschaften (wie ReadOnly oder eben auch den TypeConverter) entsprechend anpasse.

    Das funktioniert soweit ziemlich perfekt. Selbst das automatische Aktualisieren der Daten im Grid, wenn z.B. im PropertyGrid ein Wert geändert wird, klappt tadellos (nur umgekehrt hakt es manchmal, aber das ist zzt. vernachlässigbar und von niedrigerer Priorität, weshalb ich da nicht weiter drauf eingehe).
    Was mich (und unsere Kunden) allerdings massiv stört, ist die Trägheit des ganzen. Beim Laden des Grids, selbst bei kleinsten Aktualisierungen oder Filtern der Zeilen im DataGrid werden quasi jedes Mal sämtliche Properties sämtlicher Items erneut gelesen, und das scheint mächtig Performance zu fressen. In einem Fall dauert das Laden eines Grids mit nur vier Zeilen und ca. 25 Spalten grob geschätzt 2 Sekunden.

    Also habe ich probeweise einen kleinen Umbau vorgenommen: Statt die Liste aller Positionen direkt der ItemsSource zuzuweisen, erzeuge ich nun stattdessen ein DataTable, das die gewünschten Spalten mit dem jeweiligen Typ enthält, erzeuge für jede Position eine DataRow und fülle die Rows mit den entsprechenden Daten. Am Ende wird dann DataTable.DefaultView der ItemsSource des DataGrids zugewiesen um die Daten darzustellen. Das geht deutlich(!) schneller, das Filtern des Grids ist kein Eiertanz mehr und das ganze läuft wesentlich flüssiger. Und mit "wesentlich" meine ich wirklich "wesentlich"!

    Nun zur Problemstellung:

    Erstes Problem: Ich arbeite hierbei recht viel mit CustomTypeConvertern, um eben, wie oben schon angedeutet, Maße mit Einheit ("x mm", "y°", "z mm²") auszugeben (und eingeben zu können), oder auch um Fließkommazahlen immer in einem definierten Zahlenformat auszugeben, dem Benutzer aber zu ermöglichen, wahlweise Dezimalpunkt oder Komma gleichberechtigt benutzen zu können. Oder dass Zahlenwerte, die eine bestimmte Bedeutung haben, mit einem anderen Text angezeigt werden (z.B. "0 = Auto").
    Im AutoGeneratingColumn-Ereignis des DataGrids bekomme ich bei Verwendung der Positionsliste als Datenquelle auch immer den entsprechenden PropertyDescriptor übergeben, über den ich den TypeConverter abfragen und der DataGridColumn, bzw. dem Binding der DataGridColumn mitteilen kann. Verwende ich jedoch das DataTable als Datenquelle, ist der PropertyDescriptor ein vom DataTable automatisch erzeugter DataColumnTypeDescriptor auf Basis der DataColumn-Definition, auf den ich keinen Einfluss mehr habe. Der DataColumn selbst kann ich aber gar keinen CustomTypeDescriptor anhängen.
    Kurzum: Ich habe nicht herausfinden können, wie ich dem DataGrid z.B. bei einer Spalte vom Typ double einen CustomTypeDescriptor unterjubeln kann, wenn ein DataTable als Datenquelle fungiert.
    Ich möchte hierbei betonen, dass ich die Spalten im DataGrid nicht manuell erzeugen will, sondern sie sollen automatisch auf Basis der Klassen-Properties (und deren Attribute) angelegt werden. Zum einen habe ich mehrere solcher Objektlisten auf verschiedenen Klassen basierend, zum anderen möchte ich die Darstellung ganz einfach und "zentral" durch Ändern der Property-Attribute sowohl für das Forms-PropertyGrid als auch für das WPF-DataGrid anpassen können.

    Zweites Problem: Die Zuordnung Tabellenzeile -> Positions-Objekt ist mit dem DataTable natürlich weggefallen, d.h. DataTable und meine Positions-Objekte sind komplett ohne Verbindung zueinander. Eine erste Idee von mir dazu wäre, in einer weiteren Spalte, die im Grid nicht dargestellt werden dürfte, einen zusätzlichen Key abzulegen, der die Verbindung wieder herstellt (z.B. die Datenbank-ID), um dann bei Änderungen am DataTable mit Hilfe der ID die geänderten Werte in das zugehörige Objekt zurückzuschreiben oder umgekehrt Änderungen im DataTable vorzunehmen, wenn sich aus anderen Gründen eine Eigenschaft einer Position ändert.

    Drittes Problem: Die Daten im Grid lassen sich nicht bearbeiten. Obwohl nur ausgewählte Spalten als "ReadOnly" markiert sind, lässt das Grid mich auch in den anderen Spalten keine Änderungen vornehmen. Vermutlich eine Frage irgendwelcher Eigenschaften der DataColumns oder DataRows, die ich noch nicht gefunden habe.

    Hat jemand dazu irgendwelche Ideen?

    Nachtrag: Problem Nummer 3 hat sich gerade erledigt. Das war ein Fehler meinerseits. Weil die Auflistung, auf die sich das Grid bezog, für das Editieren eine Sonderbehandlung brauchte, gab es hier ein BeforeCellEdit-Event, das nicht auf die neue Datenquelle angepasst war und daher das Starten des Editier-Modus grundsätzlich cancelte.
    Für Problem Nummer 1 experimentiere ich gerade mit DataColumn.ExtendedProperties, das scheint recht vielversprechend zu sein. Ist keine triviale Lösung, aber anscheinen eine gangbare. Wenn es klappt und Interesse besteht, kann ich meine Lösung ja mal in einem follow-up präsentieren.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Arby“ ()