DataGridViewEx - DataGridView mit eingebautem Filter

    • Release
    • Closed Source

    Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von us4711.

      DataGridViewEx - DataGridView mit eingebautem Filter

      Name:
      DatagGidViewEx

      Beschreibung:
      Die vorgestellte Bibliothek ersetzt System.Windows.Forms.Datagridview mit einem Control, das spaltenbezogene, kumulative Filter-Funktionalität zur Verfügung stellt.

      ###########################################################
      Achtung: Überarbeitete Version in Post #29 (2017-03-28)
      ###########################################################

      Downloads:
      US.WindowsEx.FormsEx.ControlsEx.4.2015.0326.15.zip (158,94 kB) (Bugfixes: Farbmanagement, Filterparsing) (Achtung: Property BackColor heisst nun Property BasicColor)



      Hintergrund:
      Das DataGrid-Steuerelement war für VB6 genauso, wie es das .Net DataGridView-Steuerelement heutzutage ist, eine bevorzugte Weise, strukturierte Daten anzuzeigen.
      Insbesondere bei RAD-Sprachen wie VisualBasic und C# können hiermit in kürzester Zeit vollständige Datenverwaltungen (CRUD) erstellt werden.

      Immer jedoch fehlte mir die eingebaute Möglichkeit, das angezeigte Datenvolumen "On-The-Fly" filtern zu können.
      Eine solche Funktionalität fand ich dann bei den Controls der Firma ComponentOne zu VB6-Zeiten.

      Die Kosten dafür lagen jedoch weit ausser dem mir zur Verfügung stehenden Finanzrahmen, sodass ich anfang "was Eigenes" zu programmieren. Das wurde dann doch ziemlich hart:
      • Entwurf eines erweiterten Datagridview mit passenden Entwurdfzeit-Designern
      • Entwurf eigener Datagridviewspalten mit entsprechender Filterfunktionalität
      • Entwurf der gewünschten Filterfunktionalität.
      • Entwurf eines passenden Column-Editors
      • automatische Spaltengenerierung der erweiterten Columns über die angebundene Bindingsource
      Die automatische Spaltengenerierung mit dem passenden Column-Editor war das härteste Stück Arbeit. Da muss mann WIRKLICH tief ins Framework einsteigen, MSDN und ILSPY benutzen.
      Nun aber läuft's nach meinen Anforderungen seit ca. 6 Monaten, und, soweit ich's beurteilen kann, bugfrei.

      Funktionalitäten:
      1. DataGridViewEx
        Die Filterfunktionalität wird durch eine Mausclick auf die Zelle(-1,-1) erreicht. Wenn Filtern erlaubt ist (Property FilteringEnabled), wird dort ein Filtersymbol angezeigt.
        Neben der eigentlichen Filterfunktionalität sind nach persönlichem Geschmack einige Goodies eingeflossen:
        1.1 Property AutomaticFiltering: Legt fest, ob die Anwendung der SpaltenFilter auf die Datenquelle automatisch erfolgen soll.1.2 Property :
        1.2 Property BackColor: Die Spaltenköpfe sollten in einer Art Button-Stil sein, sodass eine OwnerDraw-Funktion mit Farbverlauf und Blend-Funktionalität her. Die hier angegebene Farbe wird aufgehellt und als Startfarbe des Farbverlaufs verwendet.
        1.3 Property BasicFilter: Eine Datenquelle kann ja bereits gefiltert sein, wenn sie dem DataGridView zugeordnet wird. Dieser "Basisfilter" wird hier festgelegt und auf die Datenquell angewendet.
        1.4 Property FilterBackColor, Property FilterForeColor: Farbgebung der in der Filterzeile verwendeten, nicht selektierten Controls.
        1.5 Property FilterSelectedBackColor, Property FilterSelectedForeColor: Farbgebung der in der Filterzeile verwendeten, selektierten Controls.
        1.6 Property FilterFocusColor: Es zeigte sich, das insbesondere auf Notebook-Bildschirme das selektierte Filtercontrol schlecht erkennbar ist. Daher wird in dieser Farbe ein "FocusRectangle" um die aktive Filterzelle gezeichnet..
        1.7 Property Columns: Diese Property musste überschrieben werden, um einen eigenen DataGridViewExColumnCollectionEditorimplementieren zu können.
        1.8 Property CurrentColumnFilterText: Der Filterausdruck, der sich aus den aktuellen Filteranforderungen ergibt.
        1.9 Property FilteringEnabled: Freigabe (jedoch noch nicht Anzeige) der Filterzeile. Anzeige des Filtersymbols in Zelle(-1,-1)
        1.10 Property FilteringEnabled: Freigabe (jedoch noch nicht Anzeige) der Filterzeile. Anzeige des Filtersymbols in Zelle(-1,-1)
        1.11 Property GradientBackColorEnd, Property GradientBackColorEnd: Die automatisch aus Property BackColor generierten Farbverlaufswerte können hier überschrieben werden. Bei erneuter Zuweisung von Property BackColor werden sie jedoch auf die dortigen Werte zurückgesetzt.
        1.12 Property ShowFilterbar: Die Anzeige der Filterzeile erfolgt im Normalfall über Mouseclick auf Zelle(-1,-1), kann durch setzen dieser Property aber auch programmgesteueert erfolgen.
        1.13 Property ShowVerticalScollbar: Es gibt Scenarien, wo eine Anzeige des vertikalen ScrollBars nicht gewünscht ist. Dies kann hier gesteuert werden.
        1.14 Public Event FilterColumnTextChanged(ByVal sender As Object, ByVal e As DataGridViewExFiltertextChangedEventArgs): Dieses Event wird immer dann ausgelöst, wenn sich in irgendeiner Spalte die Filteranforderung ändert. Die Filteranforderung ist in den Argumenten des Events überschreibbar. Das Event wird nicht nur für die Spalte mit der geänderten Filteranforderung ausgelöst, sondern vielmehr für alle Spalten.
        1.15 Public Event FilterTextChanged(ByVal sender As Object, ByVal e As DataGridViewExFiltertextChangedEventArgs): Dieses Event wird immer dann ausgelöst, wenn aus den Filteranforderungen aller Spalten eine neuer Filter zusammengestellt wurde.
        1.16 Eine weitere, von aussen nicht steuerbare Funktionalität:
        Wenn dem Benutzer erlaubt wird, die Anordung der Spalten zu ändern, erfolgt das Speichern des aktuellen Status bei Aufruf von Protected Overrides Sub Dispose(ByVal disposing As Boolean)

      2. Die Unterstützung für die in einer DataTable vorkommenden Datentypen ist vollständig implementiert. Folgende DataGridViewEx-Columns werden in Abhängigkeit vom Datentyp generiert. und sind über den Column-Collection-Editor erreichbar:
      • Boolean: DataGridViewExCheckBoxColumn. Filtermöglichkeit: True, False, Indetermined (alle Datensätze sind dann ausgewählt)
      • Byte
        Decimal
        Int16
        Int32
        Int64
        UInt16
        UInt32
        UInt64
        SByte: DataGridViewExNumericTextBoxColum. Diese Spalte basiert auf der NumericTextBox. Bei der automatischen Generierung der Spalten werden Für Eingabe Minimum und Maximum die dem Zahlentype entsprechenden Werte eingesetzt. Händische Definition als z.B. Hexadezimale Anzeige ist natürlich möglich.
      • Double, Single werden zu Zeit nicht unterstützt. Hierfür ist eine Anpassung der NumericTextBox erforderlich, die später erfolgen wird.
      • Char: DataGridViewExTextBoxColum. Hier wird nur das erste eingegebene Zeichen als Filteranforderung erfasst, das keinen Vergleichsoperator darstellt
      • DateTime: DataGridViewExDateTimePickerColum. Hier ist ein DateTimePicker hinterlegt, der DBNULL-Werte als beliebigen Text anzeigen kann, und auch durch Löschen der Eingabe mit <Entf> DBNULL an die Datenquelle zurückgeben kann.
      • Guid, String, TimeSpan : DataGridViewExTextBoxColum
      • Byte(): DataGridViewExTextImageColum. Hier liegt keine Filterfunktionalität vor.

        Losgelöst von Datentypen steht eine DataGridViewExButtonColumn zur Verfügung, die über den ColumnCollectionEditor zugewiesen werden kann.
        Ebenso ist eine DataGridViewExComboBoxColumn und eine DataGridViewExLinkColumn verfügbar
      • Alle Filtereingaben werden im Sinne von [Spalte] LIKE '*FilterTerm*' (also so eine Art Contains) verwendet.
        Entprechen die ersten eingegebenen Zeichen einem der Vergleichsoperatoren:
        <>, >, <, >=, <=, =
        dann wird die folgende Eingabe interpretiert als: [Spalte] [Operand] 'FilterTerm'

      Verwendung:
      Das Control sollte in einer eigenen Registerkarte des Werkzeugkastens der Entwicklungsumgebung untergebracht werden. Dort erscheinen dann folgende Controls:
      • DataGridViewEx
      • DateTimePickerEx (kann auch selbstständig verwendet werden)
      • NumericTextBoxEx (kann auch selbstständig verwendet werden)

      Erforderliche Kenntnisse:
      Eine gute Kenntnis des DataGridView und des System.Windows.Forms-Namespaces ist hilfreich bei der Anwenung der Controls in dieser Bibliothek.
      Die Anwendung des DataGridViewEx-Controls ist jedoch ziemlich unkompliziert.

      Benutzung der Controls
      DataGridViewEx aus Werkzeugkasten auf eine Form ziehen. Tabelle aus dem Register Datenquellen der Entwicklungsumgebung auf das DataGridviewEx ziehen

      ToDo
      • NumericTextBox zur Verwaltung der Datentypen Double und Single anpassen (Priorität 2)
      • DatGridViewEnumComboBoxColumn: Verwendung von Enumerationen als Datenquelle einer DataGridViewComboboxColumn (Priorität 1)
      • Farbmanagement überarbeiten (Priorität 3)
      • Parsen des Filterausdrucks überarbeiten (Priorität 0)
      • Tab-Verhalten innerhalb der Filter-Zeile (Priorität 2)
      Verwendete Programmiersprache und IDE:
      Visual Basic .NET (IDE: VB 2013 professional)

      Systemanforderungen:
      .NET Framework 4.5 (wegen bestimmter in Reflection verwendeter Features)
      Hinweis: DLL ist kompiliert auf AnyCPU asl Release

      Systemveränderungen:
      Speichert bei Neuanordnung von Spalten aktivieren bei Dispose den aktuellen Spaltenstatus am Speicherort der Projekteinstellungen.

      Downloads: (Die aktualisierten Versionen sind am Anfang des Posts)
      Musterprojekt:
      Test DataGridViewEx.zip (305 kB)

      DLL:
      US.WindowsEx.FormsEx.ControlsEx.DataGridViewEx.zip (153 kB)

      Lizenz/Weitergabe:
      CC0

      Quellen:
      Die Entwicklung dieses Controls dauert nun schon so ziemlich 10 Jahre. Nicht Vollzeit, natürlich nicht, aber immer dann, wenn Bedarf bestand.
      Diverse Fachliteratur (Bücher, Zeitschriften, Magazine, Online etc.) zu allen denkbaren Aspekten der Betriebssysteme, Entwicklungsumgebungen, Programmierstilen usw. haben dazu geführt, dass diese Anregungen hier Niederschlag gefunden haben.
      Für die Erweiterung meines Wissens danke ich insbesondere (nein, ich erhalte KEINE Werbevergütung):
      • Microsoft, Inc.
      • Kofler, Michael
      • vb-paradise
      • und viele, vile PM mit engaierten Nutzern ganz vieler Foren.
      Edit 2015-03-25:// Dateigrössen ergänzt
      Edit 2015-03-26:// ToTo ergänzt, ToDo Prioritäten vergeben, ToDo abgearbeitet
      Bilder
      • DataGridViewExGefiltert.JPG

        38,7 kB, 815×203, 1.280 mal angesehen
      • DataGridViewExUngefiltert.JPG

        32,08 kB, 839×103, 1.159 mal angesehen

      Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „us4711“ ()

      Wie schon oben angedeutet, erfolgt das setzen der Cellfarben über die Property BackColor

      VB.NET-Quellcode

      1. <Category("DataGridViewEx")> _
      2. <Description("Beeinflusst die Column-Header und den Default-Cellstyle des Row-Templates")> _
      3. <Browsable(True)> _
      4. <EditorBrowsable(EditorBrowsableState.Always)> _
      5. Public Overrides Property BackColor() As System.Drawing.Color
      6. Get
      7. Return MyBase.BackColor
      8. End Get
      9. Set(ByVal value As System.Drawing.Color)
      10. MyBase.BackColor = value
      11. Me.GradientBackColorEnd = value
      12. Me.GradientBackColorStart = LightenColor(value, 80)
      13. Me.RowTemplate.DefaultCellStyle.BackColor = value
      14. Me.RowTemplate.DefaultCellStyle.SelectionBackColor = Me.GradientBackColorStart
      15. End Set
      16. End Property

      Die Inhertance der Cell-Styles its ganz gut in Cell Styles in the Windows Forms DataGridView Control dargestellt
      Das ändert bei mir die BackColor der Header-Zeile, nicht aber die BackColor der Zellen.

      Hier meine Versuche, dabei rechts ein Standard-DGV, dessen DefaultCellStyle.BackColor ich gesetzt hab.
      Dateien

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()


      Das ist das Ergebnis aus Deinem Musterprojekt, wenn Property Backcolor verändert wird:
      Im linken DGV auf Colors.Red, im mittleren auf Colors.LightSalmon
      Gleichzeitig wurde in beiden Grids DefaultRowHeaderCellStyle angepasst:
      Links auf SystemColors.Window, Mitte auf Color.CadetBlue

      Alle Anpassungen erfolgten im Designer, nicht in der Entwicklungsumgebung, sondern in Deinem Musterprojekt Scheint doch zu funktionieren?
      jo - ist mir unerklärlich.
      Ich schwöre - ich hab bestimmt 5 mal neue GridExes draufgezogen, und mal nur die BackColor , mal defaultStyle + BC, mal BC + DefaultStyle und alles mögliche ausprobiert. Ging sich alles nix.

      Naja, wenn solch nicht wieder auftritt, ists wohl nicht reproduzierbar, und ein netter Mod möge die diesbezügliche Konversation löschen.
      Bug bestätigt. Ursache: Unzureichendes parsen der Filtereingabe. In ToDo-Liste aufgenommen
      //Erledigt, siehe Download US.WindowsEx.FormsEx.ControlsEx.4.2015.0326.15.zip in Post#1

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

      Guten Tag,

      vielen Dank für diesen Beitrag.
      Wollte die DGV gerade ausprobieren und habe das Problem, dass sich die Suchzeile nicht einblenden lässt. Ich lade Daten über eine SQL-Anweisung in die DataTable und diese ist an das DGV gebunden. Auch über die Parameter "Automatic Filtering" & "ShowFilterBar" lässt sich diese nicht anzeigen. Hab ich da etwas vergessen einzustellen oder muss ich da etwas spezielles berücksichtigen?

      Ich habe die DGV in die GUI gezogen (über die Toolbox), starte die SQL-Abfrage und lade die Ergebnisse in die DataTable. Diese wird dann dem DGV übergeben - das ist alles was ich mache.
      Hab' ich wolhl nicht erwähnt: Ich bin bekennender Anwender der von VS zur Verfügung gestellten Mechanismen.

      Geh' doch bitte einmal wie folgt vor:
      • Ziehe ein LEERES DataGridViewEx aus der Werkzeugleiste auf das Form
      • Stelle im Datenquellenfenster eine Verbindung zu Deiner Datenbank her und übernehme die Tabellen/Abfragen die Du benötigst
      • Ziehe aus dem Datenquellenfenster die Tabelle oder Abfrage auf das DataGridViewEx
      • Erweitere und/oder erstelle im Dataset Deine zusätzlichen Abfragen
      • Berücksichtige deren Aufruf im Form.Load-Event bei der dort generierten tableadapter.Fill Anweisung
      • Entferne im DatagridviewEx. eventuelle nicht verwendete Spalteb
      • Run Programm
      • Klicke links oben im DGV auf das Filtersymbol
      • Alles gut, keine zusätzlichen Einstellungen erforderlich

      Habe ich gerade nochmal genauso mit SQLSERVEREXPRES und der Northwind-Datenbank getestet. Bitte gib doch Feedback, ob alles so läuft.
      Hallo, vielen Dank für die rasche Antwort!

      Ja das Problem ist, ich möchte nicht direkt eine Datenquelle angeben. Ich habe im Hintergrund eine eigen Klasse, die die Daten von der Datenbank holt und die Verbindung managed. Dazu übergebe ich im Konstruktor meine Daten wie DB-Name, Server, User, Passwort usw und mit einer einfachen SQL-Anweisung und einem Funktionsaufruf wird eine DataTable mit den Daten gefüllt.

      Mit "this.testgrid.DataSource = sqlcontrol.myDataTable" sowie "this.testgrid.Refresh()" werden mir die Daten aus DataTable angezeigt. Dann funktioniert der Filter halt nicht - kann das an dieser Weise wie ich die Daten an die DGV binde liegen, dass der Filter nicht funktioniert?
      Doch, kriegst Du hin.
      Das Filtering erwartet als Datenquelle ein Bindingsource. Nimm Deine Datatable und füge sie einer Bindingsource als Datenquelle zu:
      Me.MyBindingsource.DataSource=Me.MyDataTable.DefaultView
      Noch ein Hinweis: Die Bindingsource macht, neben vielem anden, das Filtern einfach-
      Wenn Du aber doch Deine Klasse direkt binden willst, so muss für die Filterfunktionalität die IBindingListView-Schnittstelle implementiert sein.
      So, ich habs mal über die BindingSource probiert. Kann das sein, dass die Filterung die Informationen über die Spalten schon beim Iinitalisieren der DGV benötigt? Weil ich lade die Informationen erst, nachdem das Fenster geladen wurde. Nach dem InitializeObject() - sprich dem Fensteraufbau - setze ich über meine SQLControl-Klasse eine SQL-Query ab und speichere die Daten in einer DataTable.

      Danach rufe ich diese Methode auf:

      C#-Quellcode

      1. private void setBinding(){
      2. this.Cursor = Cursors.WaitCursor;
      3. this.dataBindingSource.DataSource = this.sqlControl.SQLDataTable;
      4. this.dataBindingSource.ResetBindings(true);
      5. this.testgrid.Refresh();
      6. setColumnsWidth();
      7. this.Cursor = Cursors.Default;
      8. }


      Der Filter funktioniert dann nämlich nicht.
      Ich muss es nämlich auf diese Weise handeln könne, da ich im Hintergrund Datenbanken mit mehreren GB an Größe habe.

      Ich hab mein Problem gelöst, probier es gerade mit DataSets, TableAdapter usw.

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

      Eventuell eine Anregung bzw. Frage bezüglich Arbeiten mit großen Datenmengen.

      Mein Problem:
      Ich habe eine Datenbank mit 36 GB an Daten & die größte Tabelle hat knappe 6 Millionen Einträge. Dh. ich kann diese Informationsflut nicht einfach in die GUI laden.
      Jz hab ich mal keine SQL-Query beim Start des Programmes ausgeführt -> damit ist sie leer und muss beim Start nicht unendlich Daten laden. So, damit ich die Daten aber in die DGV bekomme und die Filterleiste verwenden kann, muss diese über die BindingSources bzw. TableAdapter verbunden sein.
      Da ich aber nur explizit die Daten laden will, die ich in den Filter eingegeben hab, bin ich auf den Listener "FilterTextChanged" gestoßen. Dieser wird aber nach jedem Buchstaben den ich eintippe ausgeführt - da kann ich keine ordentlich Suche Starten, denn sobald ich '<1' bei einem Datumssuchfeld eingegeben habe ist im e.FilterText-Objekt schon in Filtertext drinnen und es beginnt über eine SQL-Query in der Datenbank danach zu suchen. Gibt es die Möglichkeit einen Listener zu setzten, der erst dann abgefeuert wird, wenn eine Filter einer Spalte vollkommen eingegeben wurde - sprich ich drücke Enter bzw. Tab weil ich die Spalte wechsle?

      Mir ist eben noch ein benutzerfreundlicher Fehler aufgefallen. Wenn man in einem Filter-Textfeld drinnen ist, kann man mit der Enter bzw. Tab-Taste nicht rauswechseln, man muss immer zur Maus greifen.

      Das Konzept dieser DGV finde ich sehr gut! Mir sind diese beiden Sachen halt noch aufgefallen

      axelp schrieb:

      this.sqlControl.SQLDataTable;

      Wie ich schon sagte, die Datenquelle MUSS

      us4711 schrieb:

      IBindingListView-Schnittstelle
      implementieren, darauf baut alles auf, im Übrigen auch das Standard-Datagridview.
      Ohne diese Implementierung wirst Du nicht weiterkommen.
      Wenn's dann mit Standard-Lösungen klappt, umsobesser.
      Große Datenmengen: Auch dazu gibt's Lösungen: Eigenschaft Virtualmode und Event CellValueNeeded in der DataGridView-Definition. Damit kannst Du ganz nach Wunsch das Laufzeitverhalten anpassen. Gilt auch für DataGridViewEx, da dieses ja vom Standard-Datagridview erbt.

      axelp schrieb:

      kann man mit der Enter bzw. Tab-Taste nicht rauswechseln


      Stimmt, ist ein von mit gewolltes Verhalten. Wo sollte man denn hinwechseln? In die nächste Filterzelle, auf das Datagridview selbst, auf das nächste Control in der Tab-Reihenfolge?
      Da sind die Wünsche bei den Nutzer wohl eher zu unterschiedlich.
      Im DGV selbst hast Du ja das Standardverhalten.