Hydrogen-4 Hydrogen-4 - 11 months ago 37
Vb.net Question

How to filter an object list based on an unknown property

I created a user control consisting of a text box and data grid.
(code not included on the sample below).
I need to filter the content of the data grid by a specific column.
The name of the column depends on the name of the object properties.
The object will vary, however each control will be bound to a specific object.

How I am using the control example:

Public Class Form1

Private ObjectList As List(Of Object)
Sub New()
' This call is required by the designer.

' Add any initialization after the InitializeComponent() call.
ObjectList = GenerateRandomData()
AddHandler Me.MyUserControl.SelectionStatusChanged, AddressOf UpdateForm
AddHandler Me.MyUserControl.TextChanged, AddressOf UpdateList

Me.MyUserControl.DataSource = ObjectList
End Sub

Private Sub UpdateList()
Dim FilteredList As IEnumerable(Of Object) = From obj As Item In ObjectList
Where obj.Prop_1.StartsWith(Me.MyUserControl.Text)
Select obj
Me.MyUserControl.DataSource = FilteredList.ToList
End Sub

Private Sub UpdateForm()
If Me.MyUserControl.HasItemLoaded Then
Dim NewItem As Item = CType(Me.MyUserControl.SelectedItem, Item)
TextBox1.Text = NewItem.Prop_1
TextBox2.Text = NewItem.Prop_2
TextBox3.Text = NewItem.Prop_3
End If
TextBox1.Text = ""
TextBox2.Text = ""
TextBox3.Text = ""
End Sub

Private Function GenerateRandomData() As List(Of Object)
Dim lst As New List(Of Object)
For i = 0 To 10000
Dim itm As New Item
Dim value As Integer = CInt(Int((999999 * Rnd()) + 1))
Dim value2 As Integer = CInt(Int((999999 * Rnd()) + 1))
itm.Prop_1 = value
itm.Prop_2 = "abc " & value & value2
itm.Prop_3 = value2 & value & " abc"
Return lst
End Function
End Class

I would like to handle the work of UpdateList() inside the User control (move the code or equivalent result) but the problem is that the control is unaware of the object type so I cannot directly call Prop_1 or whatever is the name of the property from inside the user control.

So far I am accomplishing this by listening for events outside the user control but I will like to remove as much responsibility from the programmer.

Any ideas are welcome.

Answer Source

The following is designed around its SetList method that is used to set the source list (IEnumerable(Of T)) and the name of the property to filter on in the UpDateList method. It use Reflection to retrieve the desired property.

Public Class UserControl1
    Private list As IEnumerable
    Private filterPropInfo As Reflection.PropertyInfo

    Public Sub SetList(Of T)(list As IEnumerable(Of T), filterPropertyName As String)
        filterPropInfo = GetType(T).GetProperty(filterPropertyName, GetType(String))
        If filterPropInfo Is Nothing Then
            Throw New ArgumentException(String.Format("{0} is not a Public String Property on Type: {1}", filterPropertyName, GetType(T).FullName))
        End If
        Me.list = list
    End Sub

    Public Sub UpdateList()
        Dim FilteredList As IEnumerable(Of Object) = From obj In list
                                                     Where CStr(filterPropInfo.GetValue(obj)).StartsWith(Me.Text)
                                                     Select obj

        Me.DataGridView1.DataSource = FilteredList.ToList
    End Sub
End Class

Example usage by calling the test method.

Public Class Form1
    Sub test()
        Dim l As New List(Of Item)
        l.Add(New Item("fred"))
        l.Add(New Item("freddy"))
        l.Add(New Item("fredrick"))
        l.Add(New Item("joe"))
        UserControl11.[Text] = "fred"
        UserControl11.SetList(l, "Field1")
    End Sub
End Class

Class Item
    Public Property Field1 As String
    Public Property Field2 As Int32
    Public Sub New(f1 As String)
        Me.Field1 = f1
    End Sub
End Class