Rubrik: Controls · DataGrid & DataGridView | VB-Versionen: VB2005, VB2008 | 10.06.08 |
Anzeige eines Daten-Array im DatagridView Einstellungen des DatagridView zur Anzeige von Daten in einem zweidimensionalen Double-Array | ||
Autor: Manfred Bohn | Bewertung: | Views: 32.717 |
ohne Homepage | System: Win2k, WinXP, Win7, Win8, Win10, Win11 | Beispielprojekt auf CD |
Das DataGridView-Steuerelement bietet eine Vielzahl von Möglichkeiten, um Daten aller Art anzuzeigen und zu editieren. Zu diesem Zweck stellt es eine Reihe von Objekten zur Verfügung, deren Methoden und Eigenschaften aber nur mit einiger Übung zu überblicken sind und zielgerichtet angewendet werden können.
Erschwert wird die Verwendung des Steuerelements, weil manche Eigenschaften von anderen "überschrieben" werden und weil ein Teil der Objekte in Hierarchien angeordnet sind. Einstellungen der übergeordneten Objekte blockieren die untergeordneten; aber nur, falls sie auch 'gesetzt' worden sind.
Was diesem Steuerelement fehlt, sind Methoden, die auf einfache Weise jeweils eine bestimmte Anwendungsfunktionalität bereitstellen.
Zur Demonstration, was damit gemeint ist, dient die Klasse 'cArrayGrid'.
Das DataGridView wird verwendet, um den Inhalt eines Double-Array anzuzeigen.
Einige Vorüberlegungen zur Gestaltung der Funktionalität:
- Die Anordnung der Daten in einem Array wírd üblicherweise als 'fixiert' betrachtet, weil Arrays - wie Datenbanktabellen - meist Datenaggregate (z.B. Matrizen) enthalten. Bei der Anzeige ist das Hinzufügen, Löschen, Sortieren, Verschieben oder Auswählen von Zeilen und Spalten deshalb nicht zuzulassen.
- Der Datentyp 'Double' stellt einen 'breiten Datenraum' zur Verfügung. (Man vergleiche Double.Epsilon mit Double.MaxValue.) Bei der Anzeige möchte man aber übersichtliche Spalten haben, in denen der Dezimalpunkt stets an der gleichen Stelle steht. Dabei soll die Zahl der jeweils angezeigten Nachkommastellen automatisch an den Dateninhalt der Spalte angepasst werden.
Auch zu einem Mix mit Werten, die sinnvoll nur in Exponentialdarstellung angezeigt werden können, sollte es nicht kommen. Absolut extrem hohe Werte (meist ohnehin Ausreißer oder das Resultat von Berechnungsfehlern) sollen ausgefiltert sein. Die 'rohen' Daten müssen aber - z.B. in Form von Tooltips - in der Ansicht zur Verfügung stehen.
- Die Breite der Spalten sollte jeweils an den Dateninhalt angepasst sein.
(Während die Daten innerhalb einzelner Spalten oft in einem begrenzten Wertebereich liegen, trifft dies zwischen den Spalten mitunter nicht zu.)
Der Konstruktor der Klasse 'cArrayGrid' erledigt die Aufgabe.
Neben dem anzuzeigenden Array können vier optionale Boolean-Parameter übergeben werden:
- 'HeadersVisible' legt fest, ob die Array-Indices in den Zeilen- und Spaltenköpfen angezeigt (true) oder ob sie als Zell-Tooltip aufbereitet (false) werden.
- 'AllowResizing' legt fest, ob die Spaltenbreite und Zeilenhöhe mit der Maus zu ändern sind.
- 'ShowToolTips' legt fest, ob Zellen-ToolTips angezeigt werden.
- 'DisplayExponential' erlaubt die Exponentialdarstellung übergroßer Zahlen.
Schritte zur Ausführung des Anwendungsbeispiels:
- Ein neues Projekt erstellen (WindowsApplication)
- Ein neues Klassenmodul hinzufügen und den Code von 'cArrayGrid' eintragen
- In die FormLoad-Prozedur von 'Form1' den Code des Anwendungs-Beispiels eintragen
- Den Debugger starten
(Man kann natürlich eine Instanz von 'cArrayGrid' auch im Deklarationsteil des Formulars mit 'WithEvents' erstellen und deren Ereignisse verarbeiten.)
Code der Klasse 'cArrayGrid'
Option Strict On Option Explicit On ''' <summary> ''' Klasse zur Anzeige eines Double-Array in einem DataGridView-Steuerelement ''' </summary> Public Class cArrayGrid Inherits DataGridView ' Maximal angezeigte Datenausprägung (ohne Expo-Darstellung) Const cMaxValue As Double = 100000000.0 ' Maximal angezeigte Zahl der Nachkommastellen Const cMaxDigits As Integer = 10 ' Anzeige-Font Private gFont As Drawing.Font = _ New Drawing.Font("Arial", 14, _ FontStyle.Regular, GraphicsUnit.Pixel) ' Zum Spoeichern der Werte der Optionalen Konstruktor-Parameter Dim gHeadersVisible As Boolean ' Zeilen-/Spaltenköpfe sichtbar? Dim gAllowResizing As Boolean ' Zeilen-/Spaltenbreite variabel? Dim gShowTooltips As Boolean ' Anzeige von Zell-Tooltips? Dim gDisplayExponential As Boolean ' Daten in Exponentialdarstellung? Dim gArr(,) As Double ' Speicher für Array-Daten
''' <param name="arr">Double-Array (Rank = 2), ''' dessen Elemente angezeigt werden sollen</param> ''' <param name="HeadersVisible">Zeilen-/Spaltenköpfe sichtbar?</param> ''' <param name="AllowResizing">Zeilenhöhe/Spaltenbreite einstellbar?</param> ''' <param name="ShowTooltips">Anzeige von Zell-Tooltips?</param> ''' <param name="DisplayExponential">Bei übergroßen Zahlen: ''' Exponentialdarstellung zulassen</param> Public Sub New(ByVal arr(,) As Double, _ Optional ByVal HeadersVisible As Boolean = True, _ Optional ByVal AllowResizing As Boolean = False, _ Optional ByVal ShowTooltips As Boolean = True, _ Optional ByVal DisplayExponential As Boolean = False) ' Parameter-Werte speichern gHeadersVisible = HeadersVisible gAllowResizing = AllowResizing gShowTooltips = ShowTooltips gDisplayExponential = DisplayExponential If IsNothing(arr) Then Exit Sub gArr = CType(arr.Clone, Double(,)) CType(Me, System.ComponentModel. _ ISupportInitialize).BeginInit() Me.SuspendLayout() ' Grid-Grundeinstellungen InitGrid() If Not IsNothing(arr) Then ' Daten ins Grid eintragen FillGrid() ' Spaltenansicht einrichten InitColumns() End If CType(Me, System.ComponentModel. _ ISupportInitialize).EndInit() Me.ResumeLayout(True) End Sub
Private Sub InitGrid() ' Grundeinstellungen des DataGridView ' vor der Zuweisung von Array-Daten ' AlternatingRow-Hintergrundfarbe Dim gray245 As System.Drawing.Color = _ Color.FromArgb(255, 245, 245, 245) ' Cursor-Hintergrundfarbe Dim gray230 As System.Drawing.Color = _ Color.FromArgb(255, 230, 230, 230) With Me ' Grid-Einstellungen ' ================== If gHeadersVisible Then .ClipboardCopyMode = _ DataGridViewClipboardCopyMode. _ EnableAlwaysIncludeHeaderText Else .ClipboardCopyMode = _ DataGridViewClipboardCopyMode. _ EnableWithoutHeaderText End If ' Einfügen, Löschen, Umordnen verbieten .AllowDrop = False .AllowUserToAddRows = False .AllowUserToDeleteRows = False .AllowUserToOrderColumns = False .AllowUserToResizeRows = gAllowResizing ' Maus-Click auf Column/Rowheader soll NICHT ' zur Markierung führen .SelectionMode = _ DataGridViewSelectionMode.CellSelect ' Keine Auswahl erlauben, weil ' Daten nur angezeigt werden .MultiSelect = False .BorderStyle = _ Windows.Forms.BorderStyle.Fixed3D ' Farbe des nicht mit Zellen belegten ' GridView-Hintergrundes .BackgroundColor = Color.DarkGray ' Nur bei 'Bedarf' werden die Scrollbars angezeigt ' (durch eine andere Einstellung dieser Eigenschaft ' kann man sie aber am Erscheinen hindern) .ScrollBars = Windows.Forms.ScrollBars.Both ' ColumnHeader-Einstellungen ' ========================== .ColumnHeadersVisible = gHeadersVisible .ColumnHeadersHeightSizeMode = _ DataGridViewColumnHeadersHeightSizeMode. _ AutoSize .ColumnHeadersBorderStyle = _ DataGridViewHeaderBorderStyle.Raised With .ColumnHeadersDefaultCellStyle .Font = gFont .ForeColor = Color.Black .BackColor = gray230 .Alignment = _ DataGridViewContentAlignment. _ TopLeft End With ' RowHeader-Einstellungen ' ======================= .RowHeadersVisible = gHeadersVisible .RowHeadersWidthSizeMode = _ DataGridViewRowHeadersWidthSizeMode. _ AutoSizeToAllHeaders .RowHeadersBorderStyle = _ .ColumnHeadersBorderStyle ' von oben übernehmen .RowHeadersDefaultCellStyle = _ .ColumnHeadersDefaultCellStyle ' Row-Einstellungen ' ================= With .RowsDefaultCellStyle ' Zahlen werden nach rechts ausgerichtet .Alignment = _ DataGridViewContentAlignment.MiddleRight .BackColor = Color.White .ForeColor = Color.Black .Format = "0.00000000" .NullValue = "???" 'NaN ' 'Cursor'-Farbe .SelectionBackColor = gray230 .SelectionForeColor = Color.Black .Font = gFont End With With .AlternatingRowsDefaultCellStyle ' für bessere Lesbarkeit der DatenZeilen .BackColor = gray245 End With ' Weitere allgemeine Eigenschaften ' ================================ .TopLeftHeaderCell.Value = "--> Clipboard" .TopLeftHeaderCell.ToolTipText = _ "Doppel-Click kopiert die Daten" .ShowCellToolTips = gShowTooltips .Cursor = Cursors.Arrow .DoubleBuffered = True .ResizeRedraw = True .GridColor = Color.DarkBlue End With End Sub
Private Sub FillGrid() ' Die Arraydaten werden in das GridView eingetragen Dim r, c As Integer ' Loops Dim wert As Double ' Inhalt Array-Element Dim tt As String ' Tooltip Dim rows As DataGridViewRowCollection = MyBase.Rows ' Daten Löschen MyBase.Columns.Clear() MyBase.Rows.Clear() ' Spalten erstellen For c = 0 To UBound(gArr, 2) ' ArrayIndex in Spaltenkopf eintragen MyBase.Columns.Add(CStr(c), "Col_" + CStr(c)) Next c ' Für jede Spalte: Anzeigeformat ermitteln Dim fmt() As String = CreateFormat(gArr) ' Die erforderliche Anzahl Zeilen in einem ' Rutsch erstellen: MyBase.Rows.Add(gArr.GetLength(0)) ' Daten ins DatagridView eintragen For r = 0 To UBound(gArr, 1) With rows(r) ' Zeileneigenschaften festlegen: ' Keine 'verschwindende' Zeile zulassen .MinimumHeight = 20 ' Fetter Strich zwischen den Zeilen .DividerHeight = 2 ' Zeile r mit Werten füllen For c = 0 To UBound(gArr, 2) wert = gArr(r, c) tt = "" ' Tooltip Initialisieren With .Cells(c) ' Array-Element: ' Inhalt der Zelle aufbereiten .Tag = wert ' Original-Wert aufbewahren If Math.Abs(wert) <= cMaxValue Then ' Anzuzeigender Wert (formatiert) .Value = Format(wert, fmt(c)) If Math.Abs(wert - CDbl(.Value)) >= _ Double.Epsilon Then ' Anzeige weicht vom Array-Wert ab: ' Wert in ToolTip eintragen tt = CStr(wert) End If ElseIf Double.IsNaN(wert) Then ' Keine Zahl: NullValue wird angezeigt .Value = Nothing tt = "Not A Number!!" ElseIf wert > cMaxValue Then If gDisplayExponential Then ' Exponentialdarstellung .Value = Format(wert, "0.##########E+000") Else ' übergroßer Wert .Value = " > MaxValue" End If tt = CStr(wert) ElseIf wert < cMaxValue Then If gDisplayExponential Then ' Exponentialdarstellung .Value = Format(wert, "0.##########E+000") Else ' zu kleiner Wert .Value = " < MinValue" End If tt = CStr(wert) End If If gHeadersVisible = False Then If tt <> "" Then tt += vbCrLf ' Array-Indizierung in Tooltip eintragen tt = "Arr(" + CStr(r) + "," + CStr(c) + ")" End If If gShowTooltips Then ' Zell-Tooltip erstellen .ToolTipText = tt End If End With Next c ' Array-Index in den Zeilenkopf eintragen .HeaderCell.Value = "Row_" + CStr(r) End With Next r End Sub
Private Sub InitColumns() ' die meisten allgemeinen Einstellungen der Spalten ' müssen in jeder einzelnen Spalte vorgenommen werden Dim c As Integer Dim col As DataGridViewColumn ' Dim cw(Me.ColumnCount - 1) As Integer For c = 0 To MyBase.Columns.Count - 1 col = MyBase.Columns(c) With col ' Fetter Spalten-Trennstrich .DividerWidth = 2 ' Sortieren der Spalte abschalten .SortMode = _ DataGridViewColumnSortMode.NotSortable If gAllowResizing Then ' User kann die Spaltenbreite mit der Maus ändern .AutoSizeMode = _ DataGridViewAutoSizeColumnMode.None .Resizable = DataGridViewTriState.True ' Einstellung der Spaltenbreite initialisieren ' (!! Diese Methode schluckt ggf. Rechenzeit !!) .Width = .GetPreferredWidth( _ DataGridViewAutoSizeColumnMode. _ AllCellsExceptHeader, True) ' keine 'verschwindende' Spalte zulassen ' zu kleines Width wird ggf. automatisch angepasst .MinimumWidth = 60 Else ' Spaltenbreite wird anhand der Daten festgelegt ' und ist fixiert .AutoSizeMode = _ DataGridViewAutoSizeColumnMode. _ AllCellsExceptHeader End If .ValueType = GetType(Double) ' nur Anzeige der Daten, kein Editieren zulassen .ReadOnly = True .Visible = True End With Next c End Sub
Private Function CreateFormat(ByVal arr(,) As Double) As String() ' Zahl der erforderlichen Nachkommastellen ' in den einzelnen Spalten ermitteln Dim r, c As Integer ' Loops Dim str As String ' Format-String Dim fmt(UBound(arr, 2)) As String ' Rückgabe ' Kulturspezifischer Dezimalpunkt Dim dec_sep As String = _ My.Application.Culture.NumberFormat. _ NumberDecimalSeparator Dim dp, nk, nk_max As Integer For c = 0 To UBound(arr, 2) fmt(c) = "0." 'G anzzahlformat nk_max = 0 For r = 0 To UBound(arr, 1) If arr(r, c) <= cMaxValue Then ' Verwendung des Standard-Dezimalpunktes ' in der Formatanweisung str = Format(arr(r, c), _ "0.################") ' Im formatierten String steht der ' kulturspezifische Dezimalpunkt dp = InStr(str, dec_sep) If dp > 0 Then nk = Len(str) - dp End If If nk_max < nk Then nk_max = nk End If Next r If nk_max > cMaxDigits Then nk_max = cMaxDigits If nk_max > 0 Then ' erforderliche Nachkommastellen fmt(c) += New String("0"c, nk_max) End If Next c Return fmt End Function
''' <summary> ''' Positionieren des DataGridView auf einem Formular ''' </summary> ''' <param name="Parent">zugeordnetes Formular</param> ''' <param name="Top">Oben (Position y-Achse)</param> ''' <param name="Left">Links (Position x-Achse)</param> ''' <param name="Width">Breite des DataGridView</param> ''' <param name="Height">Höhe des DataGridView</param> Public Shadows Sub Location(ByVal Parent As Windows.Forms.Form, _ ByVal Top As Integer, ByVal Left As Integer, _ ByVal Width As Integer, ByVal Height As Integer) MyBase.Parent = Parent MyBase.Width = Width MyBase.Height = Height MyBase.Top = Top MyBase.Left = Left End Sub
Private Sub cArrayGrid_MouseDoubleClick(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles Me.MouseDoubleClick ' Daten im HTML-Format in die Zwischenablage übertragen Dim r As Integer If e.X < Me.RowHeadersWidth And e.Y < Me.ColumnHeadersHeight Then ' Doppelclick auf die linke obere Zelle ist erfolgt: If Clipboard.ContainsData(DataFormats.Text) Then _ If MsgBox("Zwischenablage enthält bereits Text - Löschen?", _ MsgBoxStyle.Question Or MsgBoxStyle.OkCancel, _ "cArrayGrid") = MsgBoxResult.Cancel Then Exit Sub My.Application.DoEvents() ' Alle Zeilen markieren Me.SelectionMode = DataGridViewSelectionMode.FullRowSelect Me.MultiSelect = True For r = 0 To Me.RowCount - 1 MyBase.Rows(r).Selected = True Next r ' Die markierten Daten werden in die Zwischenablage kopiert ' Im HTML-Format kopiert und dann in eine Datei eingetragen, ' lassen sich die Daten ganz ordentlich im Browser betrachten ' Das direkte Einfügen in ein Excel-Arbeitsblatt ist möglich Clipboard.SetDataObject( _ Me.GetClipboardContent.GetData(DataFormats.Html), True) ' Markieren von Zeilen wieder sperren Me.SelectionMode = DataGridViewSelectionMode.CellSelect Me.MultiSelect = False Me.Update() MsgBox("Die Daten sind in die Zwischenablage kopiert worden", _ MsgBoxStyle.Information, "cArrayGrid") End If End Sub
''' <summary> ''' Abfrage eines Array-Elements ''' </summary> ''' <param name="RowIndex">Index 1. Arraydimension</param> ''' <param name="ColumnIndex">Index 2. Arraydimension</param> ''' <returns>Inhalt des Array-Elements: Double-Wert</returns> Default Public Shadows ReadOnly Property Item(ByVal RowIndex As Integer, _ ByVal ColumnIndex As Integer) As Double ' Überschattung der Standard-Eigenschaft des DataGridView ' Sie wird 'ReadOnly' und liefert einen Double-Wert zurück, ' ---> den Inhalt des entsprechenden Array-Elements ' (deshalb: geänderte Parameter-Reihenfolge!) Get Try Return CDbl(MyBase.Rows(RowIndex).Cells(ColumnIndex).Tag) Catch ' falls ungültige Indices verlangt werden Return Double.NaN End Try End Get End Property
End Class
Das Anwendungsbeispiel
Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' ========================================== ' Anwendungsbeispiel ' ========================================== ' Einige Formular-Eigenschaften ... Me.MinimumSize = New Drawing.Size(500, 500) Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D Me.Visible = False ' Einige Daten erstellen Dim M As Integer = 15 Dim N As Integer = 1500 Dim arr(N, M) As Double Dim r, c As Integer For c = 0 To M For r = 0 To N arr(r, c) = 1000000000 * Rnd() + Math.Round(Rnd(), c) arr(r, c) = Math.Round(Rnd(), c) arr(r, c) = r + c + arr(r, c) Next r Next c ' SO EINFACH könnte BASIC sein: ' ============================= ' Der Konstruktor der Klasse befördert die ' Array-Daten ins DataGridView und setzt Eigenschaften Dim ag As New cArrayGrid(arr, , True) ' Die Location-Methode ordnet SE dem Formular zu, ' positioniert es und richtet seine Größe ein ag.Location(Me, 10, 10, Me.Width - 30, Me.Height - 50) Me.Show() ' ========================================== ' Ende des Anwendungsbeispiels ' ========================================== End Sub End Class