Ein VB NET DataGridView füllen

VB NET DataGridView füllen
VB NET DataGridView füllen

Daten in ein VB NET DataGridView füllen

Willkommen zum heutigen Beitrag „VB NET DataGridView füllen„!

Tja da sind wir am heutigen Tage wieder mit einer neuen Problemstellung, willkommen zurück 😛!

Du möchtest also in Visual Basic NET das typische Tabellen-Steuerelement namens DataGridView mit Daten füllen!?

Dann lass uns auch gleich schon direkt loslegen!

Die erste sich stellende Frage an dieser Stelle ist natürlich die goldene Frage Nr. 1.

Frage Dich also zurst: „Woher kommen meine Daten!?“ – Aus einer Datenbank? Aus einer Textdatei!?

Nur zur Darstellung!

Wie eigentlich immer, sind die Steuerelemente natürlich nur zur letztendlichen visuellen Darstellung da.

Sie selbst sollten für gewöhnlich keine Daten darstellen“ – wenn Du verstehst was ich meine!

Besonders in anderen Sprachen und „Stackswie z. B. WPF wirst Du diese Herangehensweise kennen- und lieben lernen.

Denke an das Beispiel anhand einer simplen TextBox, Welche einen Vornamen verarbeitet:

Wenn Du einen Vornamen in Deinem Programm verarbeitest, dann sollte nicht die TextBox diesen Vornamen widerspiegeln!

Letztendlich wäre es gut, wenn Du eine dahinter geschaltete Eigenschaft hast, Welche dann den Vornamen beinhaltet.

Die TextBox wird lediglich angewiesen, diesen Vornamen visuell darzustellen.

Sie sagt praktisch: „Hey guck mal, das befindet sich im Hintergrund in den Daten“.

Was Sie allerdings nicht sagen sollte ist: „Ich >bin< die Daten„!

So sollte es auch bei fast allen anderen Steuerelementen und im Optimalfall auch dem DataGridView als Tabelle sein.

Der gute alte Weg – Ein VB NET DataGridView füllen

VB NET DataGridView füllen - Der gute alte Weg
VB NET DataGridView füllen – Der gute alte Weg

Kommen wir im ersten Beispiel zum „guten alten Weg„, Welcher es eigentlich nicht ist, also gut..

Warum „er“ dennoch als „gut“ bekannt ist? Naja, weil es wie immer die Routine der Menschen ist, Welche etwas als gutbetitelt.

Man ist es durchaus als Anfänger gewohnt, dem DataGridView direkte Kommandos via „Items“-Auflistung zu geben.

Dies kommt – zu meinem Bedauern – viel zu häufig vor, leider ist es nicht so toll, wie es einfach erscheint.

Was Objekte gemeinsam haben..

Das funktioniert deshalb so einfach, weil die „Items“-Auflistung vom Typ „DataGridViewRowCollection“ ist.

Und naja was soll ich sagen, daher ist die Auflistung eine Zusammensetzung aus einzelnen „DataGridViewRows„.

Diese einzelnen Reihen sowie die Auflistung als solches haben somit wie jede Objekt-Instanz:

  • Eigenschaften
  • Methoden (Funktionen, Subs)
  • Ereignisse

Schaue Dir also für das erste Beispiel die Methoden der „DataGridViewRowCollection“-Klasse an.

Dort finden wir die verschiedenen „Add„-Methoden, Welche – wie der Name schon sagt – für das Hinzufügen zuständig ist.

DataGridView zusammenbauen

Bevor wir im ersten Beispiel nun Daten einfügen können, müsstest Du zuerst noch ein DataGridView anlegen.

Erstelle also nun im nächsten Schritt das DataGridView, Welches dann gleich mit Daten gefüllt wird.

Nimm gerne dieses hier als Beispiel:

VB NET DataGridView füllen - Erstes Beispiel DataGridView
VB NET DataGridView füllen – Erstes Beispiel DataGridView

Beachte jedoch, dass ich wie fast immer folgende Eigenschaften gesetzt habe.

Diese benötige ich persönlich fast in 95% der Projekte..

  • AllowUserToAddRows – False
  • AllowUserToDeleteRows – False
  • AllowUserToResizeRows – False
  • AlternatingRowsDefaultCellStyle – BackgroundColor auf LightGray
  • BackgroundColor – White
  • Dock – Fill
  • MultiSelect – False
  • ReadOnly – True
  • RowHeadersVisible – False
  • SelectionMode – FullRowSelect
  • ..und dann noch die obigen Spalten!

Daten/Reihen hinzufügen

Schauen wir uns nun die verschiedenen Überladungen der „Add“-Methode an.

Ich werde hier natürlich nicht alle durchgehen, da Du Sie ja in der obigen Dokumentation ansehen kannst.

Folgende ist allerdings die meiner Meinung nach für dich – im ersten Beispiel – die Interessanteste Überladung:

Wie Du anhand der Signatur erkennen kannst, möchte diese Überladung ein (param) Array von Objekten.

Durch die spezielle Definition müssen wir auch nicht erst ein Array erstellen und Werte einfügen.

Stattdessen können wir die Werte direkt getrennt durch Kommas auflisten.

Hardcoded

Passend zu unserem designten DataGridView würde das Hinzufügen von Daten also so aussehen:

dgvArticles.Rows.Add(1, "Article 1", "KG", 9.95, 16.49, 50)

Natürlich könnte man das auch in einer Schleife aufrufen.

Obwohl das zumindest in diesem Beispiel nicht den größten Sinn macht, zeige ich es trotzdem mal.

Wir fügen einfach mal 5 Einträge durch eine zählergesteuerte Schleife hinzu.

Dabei benutze ich meine sehr gemochte „StringInterpolation“ (mit dem Dollar-Zeichen).

Damit kann ich die Strings ein wenig einfacher zusammenbauen, als ständig „&…“ zu benutzen.

Als Schleife – Beispiel

For i = 1 To 5
    dgvArticles.Rows.Add(i, $"Article {i}", "KG", 9.95, 16.49, 50)
Next

Ich meine normalerweise müssten die Einträge vermutlich sowieso von einer Art Datenquelle kommen.

Ob man nun eine Datenbank, eine Textdatei, oder sonstige Dinge verwendet..

Aus einem Dialog heraus

Nun wird es hier schon ein wenig interessanter, da wir nun einmal die Eingaben aus einem Dialog entgegennehmen.

Füge dem Projekt nun eine neue Form hinzu und benenne Sie z. B. als „frmArticle“.

Danach kannst Du Sie analog zu meinem Beispiel designen (lad Dir am besten das Beispiel herunter):

VB NET DataGridView füllen - Anhand eines Dialogs
VB NET DataGridView füllen – Anhand eines Dialogs

Danach sorgen wir dafür, dass der Dialog auch wirklich angezeigt und dessen Ergebnis verarbeitet wird.

Gehe dazu in den „Form1„-Code und erstelle einen neuen Ereignishandler für den „Add row“-Button:

    Private Sub btnAddRow_Click(sender As Object, e As EventArgs) Handles btnAddRow.Click
        Dim dialog = New frmArticle()
        Dim result = dialog.ShowDialog(Me)
        If result <> DialogResult.OK Then
            Return
        End If
        Dim id = Convert.ToInt32(dialog.nudId.Value)
        Dim name = dialog.tbName.Text.Trim()
        Dim unit = dialog.cbbUnit.Text
        Dim purchasingPrice = dialog.nudPurchasingPrice.Value
        Dim sellingPrice = dialog.nudSellingPrice.Value
        Dim stock = Convert.ToInt32(dialog.nudStock.Value)
        dgvArticles.Rows.Add(id, name, unit, purchasingPrice, sellingPrice, stock)
    End Sub

Zuerst instanziieren wir hier eine neue Dialog-Instanz.

Danach zeigen wir diese Instanz an, indem wir die „ShowDialog„-Methode aufrufen und Me“ (also die aktuelle Form) als „Papa“ mitgeben.

Den Rückgabewert der Funktion speichern wir in der Variable „result„, um dann zu checken, ob’s „OK“ war.

Falls nicht, verlassen wir den Ereignishandler mit einem Early-Return.

Anschließend ziehen wir uns die einzelnen Daten aus den jeweiligen Steuerelementen.

Auch hier könnte man mit Datenbindung arbeiten und so mit Eigenschaften arbeiten, aber das lassen wir hier erstmal..

Zum Schluss fügen wir die Daten dem DataGridView wie oben bereits gezeigt hinzu.

Gehen wir aber noch einmal zurück zum Artikel-Dialog selbst.

Dort brauchen wir zuerst den Handler des Abbrechen-Buttons:

    Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
        DialogResult = DialogResult.Cancel
    End Sub

Der setzt das „DialogResult“ der Form, woraufhin die Form dann mit diesem Ergebnis geschlossen wird.

Danach legen wir den Code für den „Hinzufügen„-Knopf fest:

    Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
        If Not ValidateInput() Then
            Return
        End If
        DialogResult = DialogResult.OK
    End Sub

Dort prüfen wir den Rückgabewert der gleich noch folgenden Funktion und „early returnenhier wieder, falls Sie „False“ zurückgibt.

Anschließend setzen wir das „DialogResult“ auf „OK„, woraufhin auch hier die Form geschlossen wird.

Zum Schluss kommt dann hier noch die kleine „ValidateInput„-Funktion:

    Private Function ValidateInput() As Boolean
        ' dont care about id..

        Dim name = tbName.Text.Trim()
        If String.IsNullOrWhiteSpace(name) Then
            MessageBox.Show("Please provide an article name")
            Return False
        End If

        Dim unit = cbbUnit.Text
        If String.IsNullOrWhiteSpace(unit) Then
            MessageBox.Show("Please provide an article unit")
            Return False
        End If

        ' prices are handled by the numeric up downs..
        ' stock is handled by numeric up down

        Return True
    End Function

Die Id wird hier erstmal nicht weiter geprüft, da das „NumericUpDown“ schon notwendige Eigenschaften gesetzt hat.

Somit übernimmt es die Prüfung für „Min-“ und „MaxValue„, sowie den Dezimalstellen.

Das gilt übrigens auch für die anderen NumericUpDowns“ auf der Form.

Die einzigen beiden zu prüfenden Werte wären der Name und die Einheit.

Dort checken wir einfach, dass die Werte nicht leer“ sind.

Wenn alles okay war, kommen wir ans Ende der Funktion und geben True“ zurück.

Add(x, y, z) sieht blöd aus, oder!?

Ich meine man muss kein Sherlock Holmes sein, um zu sehen, dass das irgendwie blöd aussieht:

dgvArticles.Rows.Add(1, "Article 1", "KG", 9.95, 16.49, 50)

Denkst Du z. B., dass Du auch noch in einem Jahr weißt, was die 50 bedeutet?

Ist es vielleicht die Kilogramm-Anzahl, oder doch eine Mindestbestellmenge?

Was passiert zum Beispiel, wenn sich die Spalten-Reihenfolge ändert?

Möchtest Du dann viele einzelne Stellen im Code anpassen – ne, oder!?

Es muss natürlich auch nicht immer sein, dass Du der einzige Entwickler im Bunde bist.

Denkst Du, dass dies besonders leserlich und einfach zu verstehen für andere Entwickler ist?

Zusammengefasst würde ich mal behaupten, dass es folgendes nicht ist:

  • sauber
  • leserlich
  • einfach zu verstehen
  • gut zu behalten

Ja aber..

Klar, jetzt wirst Du mir ggf. mit der Ausrede kommen:

„Ja, aber ein Objekt-Konstruktor kann doch auch lang sein und durch Kommas getrennt sein“.

Da hast Du natürlich recht, weshalb das natürlich auch ebenso der Grund ist, weshalb man „langeKonstruktoren vermeiden sollte.

Die schmerzende Länge definiert hier mit Sicherheit jedes Entwickler-Team in dessen Konventionen selbst.

Aber zum Glück gibt es genug Beiträge und Bücher darüber, weshalb die relativ einfache Antwort: „Zwischen 2-3 Parametern“ sein kann.

Besonders bei einem Model, oder einer Entität namens Artikelmuss man aber wirklich von vielen Eigenschaften ausgehen.

Daher würde ich hier allgemein nicht großartig anfangen einen Konstruktor aufzublähen..

Der modernere Weg – Das VB NET DataGridView füllen

VB NET DataGridView füllen - Die moderne Variante
VB NET DataGridView füllen – Die moderne Variante

Kommen wir im nächsten Szenario zu einer etwas moderneren Herangehensweise.

Ich würde diese Weise auch durchaus als wesentlich sauberer bezeichnen, warum – siehst Du gleich.

Du kannst auch hierfür die grafische Oberfläche aus dem Beispiel von oben verwenden.

Ggf. kopierst Du das Projekt einmal, um beide Varianten separat vorliegen zu haben.

Im nächsten Beispiel werden wir nun eine datengebundene Variante verwenden.

Das verbessert neben dem oben angesprochenen „Spalten-Reihenfolgen-Problemauch noch andere Aspekte.

Sauber(er)es Feeling

Insgesamt fühlt es sich – jedenfalls für mich und viele andere Entwickler – einfach sauberer an.

Das mag vom Empfinden her vermutlich daran liegen, dass man eine klare Trennung zwischen Daten und Oberfläche hat.

Im vorherigen Beispiel haben wir stattdessen die GUI sozusagen mit den Daten vermischt – ihh!

Auch wenn das „DataGridView“ viele Methoden zur Befüllung mit Daten unterstützt, ist Diese die von mir am meisten Genutzte.

Lass uns also im nächsten Schritt die Vorbereitungen dazu treffen!

Klasse erstellen

Yay, endlich ist es soweit und wir können zu dem Punkt kommen, der die Objektorientierung wohl am meisten prägt.

Ich spreche dabei selbstverständlich vom Thema Klassen, Welche die Baupläne von Objekten darstellen.

Erst wenn man diese Pläne „umsetzt„, entstehen daraus konkrete Objekte, mit denen man dann arbeiten kann.

Diese Objekte kann man – wie Du gleich sehen wirst – auch für unser „DataGridViewverwenden.

In Kombination brauchen wir natürlich noch irgendetwas, was diese Instanzen/Objekte der Klassen dann auflisten kann.

Eine Liste muss her

Wenn man so an Auflistungen denkt, kommen einem vermutlich zuerst Dinge wie das ganz rudimentäre Array in den Sinn.

Als nächstes macht man sich ggf. Gedanken über Listen, bzw. deren generische Variante.

Leider reichen die sehr (auch meinerseits) beliebten Listen nicht aus, da ein entscheidender Faktor fehlt.

Häufig arbeiten wir in solchen Szenarien mit Aktionen, Welche dann Einträge hinzufügen, oder eben auch löschen.

Dabei ist es natürlich von Relevanz, dass die Liste z. B. eine Entfernung eines Elements auch „nach außen kommuniziert„.

Das sollte Sie deshalb im Optimalfall tun, damit andere (vor allem grafische) Elemente, eine Chance haben zu reagieren.

Im konkreten Fall unserer kleinen tabellarischen Darstellung, könnte die Tabelle somit neu gezeichnet werden.

Das geht nur eben schlecht, wenn unsere Tabelle (also das DataGridView) davon gar nichts mitbekommt.

Die Alternative zur List Of

Wie bereits erwähnt benötigen wir eine andere Art Liste, um unsere Arbeit zu erledigen.

Wenn wir ein wenig im NET Framework graben, kommen wir auf die sogenannte „BindingList„.

Diese ist wie der Name schon suggeriert, für die Bindung von Daten geeignet.

Durch Ihre generische Art, können wir die „BindingList“ für alle möglichen Arten von Objekten verwenden.

Eine Artikel-Klasse – unseren Bauplan erstellen

Zunächst gehen wir hin und erstellen im nächsten Schritt den Bauplan für unsere Objekte, also eine Klasse.

Analog zum oben bereits veranschaulichten Beispiel, könnte die ArtikelKlasse so aussehen:

Public Class Article

    Public Property Id As Integer

    Public Property Name As String

    Public Property Unit As String

    Public Property PurchasingPrice As Decimal

    Public Property SellingPrice As Decimal

    Public Property Stock As Integer

    Sub New()

    End Sub

End Class

Danach müssen wir die definierte Klasse natürlich noch in Kombination mit der „BindingList“ verwenden.

Eine Eigenschaft für die Auflistung/Liste

Gehe also im nächsten Schritt in die Form-Klasse und lege dort eine neue Eigenschaft namens „Articles“ an:

Public Property Articles As BindingList(Of Article)

Anschließend instanziieren wir diese Eigenschaft im Konstruktor der Form.

Ansonsten würden wir natürlich eine Nullverweisausnahme bekommen, sprich ein Objekt verwenden, Welches (noch) „nicht existiert“.

    Sub New()
        InitializeComponent()
        Articles = New BindingList(Of Article)
    End Sub

Danach können wir im Load-Ereignishandler dem „DataGridView“ die Datenquelle zuordnen.

Anschließend laden wir mit der gleich folgenden Methode ein paar Testdaten.

Diese würde im Normalfall eventuell aus einer Datenbank, Textdatei, oder woher auch immer kommen.

Für unser Beispiel lade ich die Daten allerdings bewusst aus einer Methode heraus..

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        dgvArticles.DataSource = Articles
        LoadArticleTestData()
    End Sub

    Private Sub LoadArticleTestData()
        Articles.Add(New Article() With {
            .Id = 1,
            .Name = "Article 1",
            .Unit = "KG",
            .PurchasingPrice = 9.95,
            .SellingPrice = 12.99,
            .Stock = 50
        })
        Articles.Add(New Article() With {
            .Id = 2,
            .Name = "Article 2",
            .Unit = "l",
            .PurchasingPrice = 13.95,
            .SellingPrice = 19.99,
            .Stock = 0
        })
        Articles.Add(New Article() With {
            .Id = 3,
            .Name = "Article 3",
            .Unit = "ml",
            .PurchasingPrice = 3.49,
            .SellingPrice = 6.99,
            .Stock = 20
        })
    End Sub

Beachte bitte, dass die Liste für jeden „Add-Vorgang“ ein getrenntes Ereignis auslöst.

Die Liste sagt also sowas wie „Hey, hier ist was hinzugekommen„.

Wenn Du natürlich nun Daten aus einer Datenbank lädst (30, 300, oder gar 30000 Einträge), spielt dieser Aspekt eine Rolle.

Für mich persönlich ist diese Rolle sogar ganz und gar nicht zu unterschätzen.

Wenn die Auflistung 3000 mal das „ItemAdded“ (oder ähnlich) auslöst, ist das für mich eher weniger effizient.

Stattdessen wäre es ja sinnvoll, wenn die Liste ein einziges Mal nach Abschluss des Ladevorgangs sagt:

So liebe zuhörenden Steuerelemente, bitte nun neu zeichnen, da ich mich verändert habe“.

Leider besitzt die „BindingList“ von Haus aus keine AddRange„-Methode.

Zum Glück können wir diese aber relativ einfach und zügig mit einer Extension nacharbeiten.

BindingList AddRange-Extension

Lege hierzu einen neuen Ordner im Projekt an, den Du dann z. B. „Utils“ nennen kannst.

Erstelle darin ein Modul namens „BindingListExtensions“ und füge folgenden Code ein:

Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Namespace Utils

    Module BindingListExtensions

        <Extension()>
        Public Sub AddRange(Of T)(bindingList As BindingList(Of T), collection As IEnumerable(Of T))
            Dim oldRaiseEvents = bindingList.RaiseListChangedEvents
            bindingList.RaiseListChangedEvents = False
            Try
                For Each item In collection
                    bindingList.Add(item)
                Next
            Finally
                bindingList.RaiseListChangedEvents = oldRaiseEvents
                If bindingList.RaiseListChangedEvents Then
                    bindingList.ResetBindings()
                End If
            End Try
        End Sub

    End Module

End Namespace

Die „BindingList„-Klasse wird hiermit dann um die „AddRange„-Methode erweitert.

In der Methode selbst merken wir uns oben den aktuellen Wert der „RaiseListChangedEvents„-Eigenschaft.

Diese bestimmt, ob für gewisse Vorgänge dann ein „Ich habe mich verändert„-Ereignis ausgelöst werden soll.

Anschließend deaktivieren wir diesen Benachrichtigungs-Mechanismus, damit Dieser pausiert.

Danach fügen wir die einzelnen Elemente in die Auflistung ein, wobei man sich theoretisch das Try-Catch sparen könnte.

Eigentlich kann in diesem Fall nicht viel falsch gehen..

Nachdem wir alle Elemente hinzugefügt haben, setzen wir die „RaiseListChangedEvents„-Eigenschaft wieder auf den vorherigen Wert.

Danach setzen wir die Datenbindungen mit Hilfe der „ResetBindings„-Methode zurück.

Elemente/Artikel mit AddRange hinzufügen

Final konnten wir so dann das zigfache Auslösen der Ereignisse unterbinden und ein effizienteres Hinzufügen umsetzen.

Schauen wir uns nun einmal an, wie ein beispielhafter Aufruf der Methode aussehen könnte.

Wir haben also nach wie vor unsere instanziierte Articles„-Eigenschaft.

Ebenso rufen im Load-Ereignishandler immer noch die LoadArticleTestData„-Methode auf.

Diese Methode könnte dann so aussehen:

    Private Sub LoadArticleTestData()
        Dim myArticles = New List(Of Article)
        myArticles.Add(New Article() With {
            .Id = 1,
            .Name = "Article 1",
            .Unit = "KG",
            .PurchasingPrice = 9.95,
            .SellingPrice = 12.99,
            .Stock = 50
        })
        myArticles.Add(New Article() With {
            .Id = 2,
            .Name = "Article 2",
            .Unit = "l",
            .PurchasingPrice = 13.95,
            .SellingPrice = 19.99,
            .Stock = 0
        })
        myArticles.Add(New Article() With {
            .Id = 3,
            .Name = "Article 3",
            .Unit = "ml",
            .PurchasingPrice = 3.49,
            .SellingPrice = 6.99,
            .Stock = 20
        })
        Articles.AddRange(myArticles)
    End Sub

Wir erstellen hier also erstmal eine lokale Liste für die Sub, Welche eine normale Liste darstellt.

Danach fügen wir die einzelnen Artikel hinzu und verwenden zum Schluss die definierte AddRange„-Methode.

Vergiss hier nicht die notwendigen Imports oberhalb der Form hinzuzufügen:

Imports System.ComponentModel
Imports VBDataGridViewFillExample.Utils

Public Class Form1

' ...

End Class

Downloads

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert