BindingSource Filtern mit einer Liste/Array

  • VB.NET
  • .NET (FX) 4.5–4.8

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

    BindingSource Filtern mit einer Liste/Array

    Hi,

    ich nutze die Winform Helpers von @ErfinderDesRades mit derm BindingSource-Erweiterung FilterX. Soweit so gut.
    Kann man damit (oder anders) auch eine Liste oder Array als Werte an den Filter übergeben?

    Ich möchte in einer Tabelle nach Datensätzen suchen. Das ID-Feld bzw. dessen Werte der Ergebnisliste sollen als Filter für eine BindingSource einer anderen Tabelle sein.
    Danke.

    Viele Grüße
    Oliver
    di BindingSource.Filter - Property reicht den Filter-Ausdruck nur weiter an die eigentliche DataSource - also üblicherweise an DataView.
    Und Dataview implementiert System.ComponentModel.IBindingListView - und das ist der Grund, warum man mit so bescheuerten String-Ausdrücken filtern kann.
    Guck dir das im Objectbrowser an.
    Langer Rede kurzer Sinn: Liste (was immer damit gemeint sein mag) oder Array implementieren IBindingListView nicht, und deshalb ist mit dene auch nix mit in dieser Weise filtern.
    Ausser, du hast eine Liste am Wickel, die IBindingListView doch implementiert, aber das halte ich für unwahrscheinlich.
    Oha, böhmische Dörfer.
    Ich habe keinen Schimmer, wie man einer Auflistung/Array so ein Interface implementiert, also lass ich es.
    Würde es Sinn ergeben (weil zur Lösung führen), wenn ich danach suche, die Auflistung/Array über einen Join an die BindingSource zu flanschen und damit zu filtern?

    Wie gesagt:
    Ich möchte in einer Tabelle nach Datensätzen suchen. Das ID-Feld bzw. dessen Werte der Ergebnisliste sollen als Filter für eine BindingSource einer anderen Tabelle sein.


    Hast du eine Lösung für dieses Problem?
    Ja, aber nicht sehr hübsch:

    VB.NET-Quellcode

    1. bs.Filter = "PatientId in (155,156,159,...)"
    Ich hab schon Filter generiert, die ca. 100 Ids auf diese Weise einzeln aufführten - keine Performance-Probleme (zu meiner eigenen Überraschung).
    Aber ich gehe davon aus, dass da iwann doch Ende Fahnenstange erreicht sein muss.
    Hmm, das ist ja einfach.
    Kann man die Auflistung nicht durch einem Ausdruck ersetzen?
    Irgendwo hast du mal sowas aufgezeigt ... Ach guck, hier isses ja. Nur verstehen tu ich es noch nicht.

    Jetzt hab ich das mal versucht, irgendwie auf mein Problem zu übertragen. Ja ja, war leider nicht so erfolgreich :(

    Das DataSet kann man hier anschauen.
    Ich hab auf einer Form ein Feld, in das ich einen Wert für Anzahl Tage eingeben kann.
    Ich möchte auf der ICa_x_Melder Tabelle sowas in der Art machen:
    select Id_ICa from ICa_x_Melder where now()-[KontaktAm]>" + Tage + " and [FeedbackAm] is null
    Also die Id_ICa der Datensätze, bei denen das KontaktAm Datum weniger als Tage zurückliegt und es noch kein Feedback gab.
    Das soll dann die BindingSource filtern.

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

    Büschen gelesen und noch mal was probiert:

    VB.NET-Quellcode

    1. If filterTage.Text <> String.Empty Then
    2. Dim idMenge = From ixm In ICaDts.ICa_x_Melder
    3. Where Not IsDBNull(ixm.KontaktAm) AndAlso DateDiff(DateInterval.Day, ixm.KontaktAm, Now()) > CInt(filterTage.Text)
    4. Select ixm.Id_ICa
    5. Dim ids = New HashSet(Of Integer)(idMenge)
    6. Dim linqfilter = From ica In ICaDts.ICa Where ids.Contains(ica.Id_ICa)
    7. ICaBS.DataSource = linqfilter.AsDataView()
    8. ICaBS.DataMember = ""
    9. End If


    Das klappt erst mal vorzüglich wie gewünscht.

    Jetzt habe ich das Problem, die BindingSource wieder auf ihren ursprüngliche DataSource umzustellen, um den "Filter" (is ja gar keiner mehr) zu entfernen:

    VB.NET-Quellcode

    1. ICaBS.DataSource = ICaDts
    2. ICaBS.DataMember = "ICa"


    Da wirft der Debugger eine Exception
    System.ArgumentException: "An die Eigenschaft oder Spalte Bemerkung für die DataSource kann nicht gebunden werden.
    Parametername: dataMember"


    und steht auf der Zeile ​ICaBS.DataSource = ICaDts

    Komisch, ging doch vorher. Die BindingSource hat per Designer genau diese DataSource und DataMember Eigenschaften verpasst bekommen.

    Das zweite noch etwas ärgerlichere Problem ist, wenn das Feld KontaktAm leer ist (Typ Date und leer heißt da DBNull), dann schmiert die LINQ Abfrage (nennt man das so, ja?) ab:
    System.Data.StrongTypingException: "Der Wert für Spalte KontaktAm in Tabelle ICa_x_Melder ist DBNull.
    Innere Ausnahme:
    InvalidCastException: Ungültige Konvertierung von Typ DBNull in Typ Date."


    Daher prüfe ich doch extra mit IsDBNull() ?(
    IsKontaktAmNull() ja klar :whistling:

    [line]-[/line]

    MVB (Microsoft Visual Basic) rausschmeißen - aus VB? Hmmm ... :/
    Und statt dessen in .NET programmieren.

    Mach ich sofort. Würdest du das ganze ein bisschen ausformulieren, bitte?
    Ich habe keine Ahnung, was du meinst und wo das im Code genau auftaucht.

    Ach, hahaha, ich hab es gesehen:
    :cursing:

    Wie ist denn die Entsprechung für InputBox(), Now(), DateDiff(), DateFormat, DateInterval, FormatDateTime() ?

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „OliverSte“ ()

    Du könntest alles mit dem Präfix Microsoft.VisualBasic. versetzen. Denn bei einigen Kandidaten wird's schwierig.
    VB6
    VB.Net
    InputBox
    Eigenbau
    Now()
    DateTime.Now
    DateDiff, ggf. auch DateInterval
    TimeSpan, die automatisch entsteht, wenn man Date1 - Date2 "rechnet")

    die anderen hab ich bisher nicht genutzt, aber den anderen fällt da bestimmt noch was ein
    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.
    Danke, @VaporiZed, genau so hatte ich es mittlerweile auch gemacht ;)
    Was spricht denn dagegen, die ein oder andere Funktion aus MVB zu benutzen?

    @ErfinderDesRades
    Ich "merke" mir die DataSource/DataMember Eigenschaften der ICaBS, indem ich in der Form-Klasse Variablen dazu deklariere und in .Load kopiere

    VB.NET-Quellcode

    1. ICaBSDataMember = Me.ICaBS.DataMember
    2. ICaBSDataSource = Me.ICaBS.DataSource


    Beim Filter-Reset dann

    VB.NET-Quellcode

    1. ICaBS.DataSource = ICaBSDataSource
    2. ICaBS.DataMember = ICaBSDataMember

    bringt mich nicht weiter.

    Ich bin ganz nah dran, die DataSource gleich vorweg über LINQ zu füttern und die ggfs. nur zu ändern.

    zu MVB gugge Visual Studio - Empfohlene Einstellungen
    (ich hab schon so viel mit dir gepostet, da hätte ich gedacht, den kennste schon, weil damit verschone ich niemanden)

    ansonsten bist du ja ein beispiel, wie MVB verhindert, dass man lernt, .Net zu programmieren: hampelst da mit DateDiff herum, während man in .Net eine Zeit-Differenz einfach so bildet:

    VB.NET-Quellcode

    1. dim delta = date1 - date2

    Und IsDbNull ist ja auch eine MVB-Krücke. Schmeiss den Sch... raus.
    Übrigens wirklich rausschmeissen geht garnet, aber man kann den GeneralImport entfernen, dann drängelt sich der Müll nicht immer vor.
    Ansonsten spricht nichts dagegen, ausgesuchte, nützliche MVB-Elemente zu nutzen (nur sind das nicht viele) - kann man ja auch, auch wenn der GeneralImport weg ist. Steht alles im verlinkten Tut.
    Jaaa, ich hab ja schon viel von dir gelesen, aber eben nicht alle just 30.957 Beiträge allein hier. Das ist wohl ein Fehler und wird natürlich asap nachgeholt :whistling:

    Wollen wir jetzt mal wieder zurück von dem wirklich sehr interessanten Exkurs MVB zum Thema und der noch offenen Frage.
    Warum kann ich die original DataSource nicht wiederherstellen?
    ähm - keine Ahnung. Ich hätt gedacht das geht.
    Ich hab sogar iwo eine entsprechende Extension:

    VB.NET-Quellcode

    1. <Extension()> _
    2. Public Sub ChangeBinding(bs As BindingSource, dataSource As Object, datamember As String)
    3. With bs
    4. .RaiseListChangedEvents = False
    5. .DataSource = dataSource
    6. .DataMember = datamember
    7. .RaiseListChangedEvents = True
    8. .ResetBindings(False)
    9. End With
    10. End Sub
    Ich hatte schon in diese Richtung gedacht, aber die falschen Methoden probiert, .SuspendBinding und .ResumeBinding isses nämlich nicht.

    Die RaiseListChangedEvents müssen wohl kurzzeitig abgeschaltet werden, dann klappt's auch mit dem Nachbarn - ähh Wechsel der DataSource ^^

    Wieder einmal zeigt sich, wie formidabel @ErfinderDesRades seine Extensions in den Winform Helpers sind :thumbsup:

    Ähh, eine Frage hätte ich noch zu dem LINQ Gedöns. Wie man sehen kann, habe ich dann doch Teile aus deinem LinqToDataSet Tut übernommen, nämlich den HashSet Trick. Ist das überhaupt nötig oder wozu ist das nötig?

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

    Hashset ist eine Auflistung, die 2 Aufgaben hervorragend umsetzt:
    • Man kann Dinge hineintun, so oft man will - Dubletten werden ignoriert.
    • Man kann blitzgeschwind mit .Contains() abfragen, ob ein Item schon drin ist, oder nicht.
    Der Hashset-Trick nutzt letzteres.
    Gugge post#6, zeile#6:

    VB.NET-Quellcode

    1. Dim linqfilter = From ica In ICaDts.ICa Where ids.Contains(ica.Id_ICa)
    das ist ein Filter, der true ergibt, wenn die Id_Ica im Hashset enthalten ist.
    Anders gesagt: Er ergibt False, wenn die Id_Ica nicht im Hashset enthalten ist.
    Aus diesem Filter wird ein DataView erzeugt (zeile#8) - ja das DataView beinhaltet folglich nur die Icas, deren Id_Ica im Hashset notiert wurde.
    Ok, kapiert. Im Wesentlichen dient der Trick also einer guten Performance, denn es geht ja auch ohne HashSet:

    VB.NET-Quellcode

    1. ​Dim linqfilter = From ica In ICaDts.ICa Where idMenge.Contains(ica.Id_ICa)


    In dem Beispiel ist der Trick wahrscheinlich sogar schädlich, denn idMenge enthält ja auch nur Integer.
    Nun, steht zwar auch oben, aber hier noch mal.
    idMenge ist vom Typ EnumerableRowCollection(Of Integer)

    VB.NET-Quellcode

    1. ​If filterTage.Text <> String.Empty Then
    2. Dim idMenge = From ixm In ICaDts.ICa_x_Melder
    3. Where Not IsDBNull(ixm.KontaktAm) AndAlso DateDiff(DateInterval.Day, ixm.KontaktAm, Now()) > CInt(filterTage.Text)
    4. Select ixm.Id_ICa
    5. 'Dim ids = New HashSet(Of Integer)(idMenge)
    6. Dim linqfilter = From ica In ICaDts.ICa Where idMenge.Contains(ica.Id_ICa)
    7. ICaBS.ChangeBinding(linqfilter.AsDataView(), "")
    8. End If


    Das funktioniert auch, ohne HashSet.

    OliverSte schrieb:

    EnumerableRowCollection(Of Integer)
    Das kann nicht sein.
    Wenn du in ObjectBrowser guckst:

    OV schrieb:

    Public Class EnumerableRowCollection(Of TRow)
    Inherits System.Data.EnumerableRowCollection
    Member of System.Data
    Summary:
    Represents a collection of System.Data.DataRow objects returned from a query. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.

    Type parameters:
    TRow: The type of objects in the source sequence, typically System.Data.DataRow.
    Der TypParameter soll eine DataRow sein. Integer ist aber keine DataRow.

    Vlt. dieses kann helfen: Video-Tut: Welchen Datentyp hat das Objekt?

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

    Ist ja verrückt =O
    Mein IntelliSense ist da aber anderer Meinung:


    Und funktioniert ja auch :rolleyes:

    Nun ist mir aufgefallen, dass ich gar nicht deine wie ich finde fabelhafte ChangeBinding Erweiterung nutzte. Na sowas, ham die von MS wohl mal wieder beim @ErfinderDesRades abgekupfert :D

    Aber warum auch immer hab ich jetzt das Problem, den Filter nur genau EINMAL anwenden zu können. Also, wenn ich die BindingSource mit ChangeBinding wieder auf den Urzustand zurücksetze und dann nochmal diesen Filter anwenden möchte, geht's nicht. Das linqfilter-Objekt enthält aber genau das, was ich erwarte! Das teste ich mit den paar Zeilen im unteren Bereich des Bildes.

    Ich dreh noch durch hier, weil ich keinen Schimmer habe, warum das nicht geht. Nach Schließen und erneutem Öffnen der Form geht es wieder, allerdings auch nur einmal.
    Idee?

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