DataListPanel: DGV-Ersatz mit MVVM-DataBinding für WinForms

    • VB.NET
    • .NET 7–8

      DataListPanel: DGV-Ersatz mit MVVM-DataBinding für WinForms

      Hallo zusammen.

      Vorab: Wer von MVVM noch nix weiß, dem wird vieles hier wie 42 große Fragezeichen vorkommen. Vor allem wird man sich fragen, was der Aufwand soll. MVVM ist hier aber nicht das an sich zu diskutierende Thema.

      Anbei mein DataListPanel (kurz DLP) zur kritischen Beäugung und zum Ausprobieren.
      Das DataListPanel ist der Ansatz eines DGV-Ersatzes und hostet als solches eine Ansammlung von UserControls eines Typs.

      Man erstellt und formt zuerst ein UserControl (UC). Wenn man bedenkt, dass das DLP am Ende Ähnlichkeiten mit einem DGV hat, sollte man das UC so erstellen, dass es gewisse Ähnlichkeit mit einer Tabellenzeile haben sollte. Das betrifft die UC-Grundmaße und auch Designeigenschaften der internen Controls, vor allem Dock:


      Da generische Parameter in der FormX.Designer.vb nicht erlaubt sind, erstellt man im nächsten Schritt eine eigene, leere Klasse, die vom DLP erbt und als Typparameter das UC nimmt:

      VB.NET-Quellcode

      1. Friend Class MyDataListPanel : Inherits Controls.DataListPanel(Of MyUserControl)
      2. End Class


      Kompiliert man das Ganze, kann man im Designer das DLP auf's Form ziehen. Und sieht erstmal nix. Wenn man bei AppStart die Funktion CreateColumnTitlesFromUserHostedControl für die DLP-Instanz aufruft, bekommt man darin zumindest mal eine Titelleiste zu sehen, welche den Spalten eines DGVs entspricht und die Dock-Eingenschaften des UCs übernimmt.

      Ich würde das gerne per SmartTag während der Designzeit machen können, aber das ist m.E. in .NET wohl um einiges komplizierter als in .NET Framework.

      Der Inhalt kommt nun durch die Items, welche über das MVVM-Pattern eingebunden werden. Dazu brauchen wir eine eigene ViewModelklasse, in der die Items stecken, die sich das gewählte Item merken kann und die zumindest eine Aktion kann (bevorzugt als RelayCommand), welches Items der Sammlung hinzufügt:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Friend Class ViewModel
      2. Property Items As New ObservableCollection(Of Object)
      3. Property SelectedItem As ItemViewModel
      4. Property CreateNewItemRelayCommand As New RelayCommand(Sub(x) AddNewItem())
      5. Private Sub AddNewItem()
      6. Items.Add(New ItemViewModel)
      7. End Sub
      8. End Class


      Desweiteren bauen wir noch eine ItemViewModel-Klasse, die sich um die Items kümmert:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Friend Class ItemViewModel
      2. End Class


      Nun noch für das DataBinding im MainForm eine BindingSource (BS) für das MainViewModel setzen und im UC eine BS für das ItemViewModel. In die FormX.Designer.VB schreibt man an geeignete Stelle

      VB.NET-Quellcode

      1. BsViewModel.DataSource = GetType(ViewModel)
      in der MyUserControl.Designer.VB entsprechend

      VB.NET-Quellcode

      1. BsItemViewModel.DataSource = GetType(ItemViewModel)

      Im MainForm brauchen wir natürlich noch eine MainViewModel-Instanz als Property, die in der Load-Methode des MainForms als DataSource der BsViewModel gesetzt wird. Zusätzlich müssen dem DLP als DataContext noch die Items des MainViewModels mitgegeben werden:

      VB.NET-Quellcode

      1. Friend Class FrmMain
      2. Property ViewModel As New ViewModel
      3. Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
      4. BsViewModel.DataSource = ViewModel
      5. MyDataListPanel1.DataContext = ViewModel.Items
      6. End Sub
      7. End Class


      Auch im UC muss man noch etwas ergänzen:

      VB.NET-Quellcode

      1. Private Sub MyUserControl_Load(sender As Object, e As EventArgs) Handles Me.Load
      2. BsItemViewModel.DataSource = DataContext
      3. End Sub


      Sobald man das kompiliert, kann man endlich noch im Designer die DataBindings einrichten. So kann einem Button auf dem MainForm der CreateNewItemRelayCommand angeheftet werden, dem DLP kann für SelectedDataContext die SelectedItem-Property des MainViewModels mitgegeben werden (Achtung: setzt dort bei den erweiterten DataBinding-Eigenschaften den UpdateMode auf OnPropertyChanged, da beim DefaultValue OnValidation die Aktualisierung nicht zum richtigen Zeitpunkt erfolgt) und im UC können diverse Properties und RelayCommands des ItemViewModels verteilt werden.

      Das Ergebnis sieht dann z.B. so aus:

      Hier sieht man die DLP-Eigenheiten Vorselektion (das Item unter dem Cursor wird grau dargestellt), Selektion (angeklicktes Item erscheint bläulich) und inhaltliche Suche: man kann dem DLP ein Label und/oder ToolStripLabel mitgeben, welches bei Eintippen von Text einige Zeit anzeigt, was man getippt hat. Dieser Text wird dann in einer festgelegten Spalte (oder auf Wunsch in allen Spalten) gesucht und das erste passende Item selektiert.


      Ein per Command und CommandParameter angebundener RelayCommand wird ausgelöst.

      Ich freue mich auf Anregungen, Rückmeldungen und Kritik. Dass das Control selbst nicht per MVVM programmiert ist, sondern »klassisch« ist mir bewusst. Diesen Anspruch hat es aber auch nicht. Es soll im Hauptprogramm das MVVM-Leben erleichtern.

      ##########

      Ich vergaß: Der eigentliche DLP-Code ist im Ordner DlpControl, die Klasse KeyConverter, welche sich um die Umwandlung von Eingabe in Text für die Suche kümmert, kommt von stackoverflow.
      Dateien
      • DlpTestProject.zip

        (32,19 kB, 143 mal heruntergeladen, zuletzt: )
      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

      Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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