BindingListFilter - Eine Klasse zum Filtern einer Listen-Datenquelle

    • VB.NET

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

      BindingListFilter - Eine Klasse zum Filtern einer Listen-Datenquelle

      Hi
      habe letztens für ein kleines Programm eine Klasse geschrieben, die eine IBindingList filtert. Leider ist der Code mehr als 19000 Zeichen lang, daher habe ich ihn als Anhang hochgeladen.
      Die Klasse filtert die im Konstruktor angegebene Liste auf Basis des Typs T und des übergebenen Prädikats, sofern dieses gesetzt ist. Sofern nicht auf den Typ gefiltert werden soll, kann man ihn einfach auf Object setzen. Der Code ist leider nicht dokumentiert, aber das meiste sollte selbsterklärend sein. Sofern Fragen - auch zum Code - bestehen beantworte ich die natürlich gerne. Die Indexermittlung ist unperformant, das hab' ich aber im Code auch noch mal angemerkt.
      Hier noch ein kleines Beispiel, bei dem eine Liste von Strings gefiltert wird. Ich implementiere demnächst noch eine Version, bei der stattdessen der DisplayMember genommen wird:

      VB.NET-Quellcode

      1. Private _filteredList As BindingListFilter(Of String)
      2. Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      3. Dim bf As New BindingList(Of String)() 'die darunterliegende Liste der Strings
      4. _filteredList = New BindingListFilter(Of String)(bf, Function(a) a.Contains(TextBox.Text)) '1. Parameter ist die darunterliegende Liste, 2. ist das Praedikat. Wenn das Praedikat Nothing ist oder weggelassen wird, wird es einfach ignoriert
      5. ListBox.DataSource = _filteredList 'Die Datenquelle der ListBox auf die gefilterte Liste setzen
      6. End Sub
      7. Private Sub TextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox.TextChanged
      8. _filteredList.Update() 'die Liste updaten, da sich das Praedikat geaendert hat
      9. End Sub

      Ich hoffe, ich habe keine Bugs reinprogrammiert und es hilft dem einen oder anderen. Viel Spaß. ;)

      Gruß
      ~blaze~
      Dateien

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

      So, hier das Beispiel entsprechend mit DisplayMember und ValueMember:

      VB.NET-Quellcode

      1. Imports System.ComponentModel
      2. Public NotInheritable Class ValueSelector
      3. Private _converter As TypeConverter
      4. Private _displayMember, _valueMember As PropertyDescriptor
      5. Private _properties As PropertyDescriptorCollection
      6. Public Sub New(ByVal underlyingList As ITypedList) 'Properties fuer die Datenbindung aus der Liste ermitteln (funktioniert mit BindingListFilter)
      7. If underlyingList Is Nothing Then Throw New ArgumentNullException("underlyingList")
      8. _properties = underlyingList.GetItemProperties(Nothing)
      9. End Sub
      10. Public Sub New(ByVal properties As PropertyDescriptorCollection) 'Properties direkt entgegennehmen - zum Beispiel per System.ComponentModel.TypeDescriptor.GetProperties
      11. If properties Is Nothing Then Throw New ArgumentNullException("properties")
      12. _properties = properties
      13. End Sub
      14. Public Property DisplayMember As String 'Die angezeigte Property
      15. Get
      16. Return If(_displayMember IsNot Nothing, _displayMember.Name, String.Empty)
      17. End Get
      18. Set(ByVal value As String)
      19. _displayMember = _properties.Find(value, False)
      20. If _displayMember IsNot Nothing Then
      21. _converter = TypeDescriptor.GetConverter(_displayMember)
      22. If Not _converter.CanConvertTo(GetType(String)) Then _converter = Nothing
      23. Else
      24. _converter = Nothing
      25. End If
      26. End Set
      27. End Property
      28. Public Property ValueMember As String 'Der dargestellte Wert
      29. Get
      30. Return If(_valueMember IsNot Nothing, _valueMember.Name, String.Empty)
      31. End Get
      32. Set(ByVal value As String)
      33. _valueMember = _properties.Find(value, False)
      34. End Set
      35. End Property
      36. Public Function GetDisplayMember(ByVal value As Object) As Object 'Ermittle dargestellten Wert auf Basis von DisplayMember
      37. If _displayMember Is Nothing OrElse Not _displayMember.ComponentType.IsInstanceOfType(value) Then
      38. Return value
      39. Else
      40. Return _displayMember.GetValue(value)
      41. End If
      42. End Function
      43. Public Function GetValue(ByVal value As Object) As Object 'Ermittle Wert auf Basis von ValueMember
      44. If _valueMember Is Nothing OrElse Not _valueMember.ComponentType.IsInstanceOfType(value) Then
      45. Return value
      46. Else
      47. Return _valueMember.GetValue(value)
      48. End If
      49. End Function
      50. Public Function GetDisplayText(ByVal value As Object) As String 'Text zurueckgeben, der angezeigt wird
      51. Dim display As Object = GetDisplayMember(value)
      52. Return If(_converter Is Nothing, If(display Is Nothing, String.Empty, display.ToString()), _converter.ConvertToString(display))
      53. End Function
      54. End Class

      Hier wird eine ListBox _fileList auf Basis einer TextBox _filterTextBox auf Basis der DisplayMember-Eigenschaft von _fileList gefiltert, damit sie nur diejenigen Elemente enthält, die den Text enthalten.

      VB.NET-Quellcode

      1. Private _bindingListFilter As BindingListFilter(Of IO.FileInfo)
      2. Private _valueSelector As ValueSelector
      3. Public Sub New()
      4. InitializeComponent()
      5. Dim bf As New BindingList(Of IO.FileInfo)()
      6. For Each fi As System.IO.FileInfo In New IO.DirectoryInfo(Application.StartupPath).GetFiles() 'Alle bestehenden Files in die Liste
      7. bf.Add(fi)
      8. Next
      9. _bindingListFilter = New BindingListFilter(Of IO.FileInfo)(bf, Function(fi) _valueSelector IsNot Nothing AndAlso _
      10. _valueSelector.GetDisplayText(fi).IndexOf(_filterTextBox.Text, StringComparison.CurrentCultureIgnoreCase) <> -1) 'nach DisplayMember filtern
      11. _valueSelector = New ValueSelector(_bindingListFilter)
      12. _valueSelector.DisplayMember = _fileList.DisplayMember 'DisplayMember an ListBox _fileList anpassen
      13. _bindingListFilter.Update() ' da _valueSelector vorher nicht gesetzt war und sich das Praedikat geaendert hat, muss der Filter geupdatet werden
      14. _fileList.DataSource = _bindingListFilter 'Datenquelle setzen
      15. End Sub
      16. Private Sub _filterTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _filterTextBox.TextChanged
      17. _bindingListFilter.Update() 'Die Filter-Eingabe hat sich geaendert ==> BindingListFilter updaten
      18. End Sub
      19. Private Sub _fileList_DisplayMemberChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles _fileList.DisplayMemberChanged
      20. _valueSelector.DisplayMember = _fileList.DisplayMember 'Der DisplayMember der ListBox hat sich geaendert ==> ValueSelector DisplayMember neu setzen
      21. _bindingListFilter.Update() 'und Liste updaten
      22. End Sub

      Der BindingList-Filter ist übrigens dazu gedacht, sich ändernde Collections zu verarbeiten. In den gezeigten Beispielen verändert sich die darunterliegende BindingList nicht, das wird aber vom Filter automatisch mitbehandelt. So könnte man im obigen Beispiel zum Beispiel eine IBindingList implementieren, die die Dateien und Verzeichnisse in einem Verzeichnis über einen FileSystemWatcher in der Liste synchronisiert. Angezeigt werden dann nur Dateien, die sich im Ordner befinden und zum Beispiel einen bestimmten Text im Dateinamen enthalten.

      Das ganze ist ggf. noch verbesserungswürdig.

      Gruß
      ~blaze~