I have an implementation of IBindingListView that filters on a single property using the '=' operator that works fair. I am needing to expand it to something like you get with DataView.Filter. Public
I want to be able to support expressions like x.filter = "LastName = 'Thomas' AND FirstName <> 'John'".
Surely that block of code has been written hundreds of times. Anybody know where I could find it?
This is a partial listing of the existing code:
Implements IBindingListView
Dim originalCollection
As List(Of T) =
New List(Of T)
Dim SuspendOriginal
As Boolean
Protected Sub UpdateFilter()
Dim equalsPos
As Int32 = _filter.IndexOf("=")
Dim propName
As String = _filter.Substring(0, equalsPos).Trim
Dim criteria
As String = _filter.Substring(equalsPos + 1, _filter.Length - equalsPos - 1).Trim
criteria = criteria.Substring(1, criteria.Length - 2)
Dim propdesc
As PropertyDescriptor = TypeDescriptor.GetProperties(GetType(T))(propName)
If originalCollection.Count = 0
Then
originalCollection.AddRange(Me)
End If
Me.Clear()
Me.SuspendOriginal =
True
For
Each item As T
In originalCollection
Dim value
As Object = propdesc.GetValue(item)
If value.ToString = criteria
Then
Me.Add(item)
End If
Next
Me.SuspendOriginal =
False
End
Sub
End Class
-
-
I know the solution lies in parsing the _filter string and putting the three pieces into a collection.
PropertyName, Criteria and Operator.
But what would be the best way to parse through the _filter string and split it up. A "Do Loop" using IndexOf to find operators and Join Words "AND" "OR" or maybe just "AND" I think allowing "OR" complicates it quite a bit more.
-
Filter is trivial with LINQ and expression trees. For pre-3.5, take a look at Subsonic, nHibernate, or any ORM mapper and see how they implemented filtering.
-
This is what I have came up with. It is still somewhat limited in scope and a little rough around the edges but works for equality expressions involving multiple properties.
Private Class Expression
Friend PropertyName As String
Friend Criteria As String
Friend Condition As String
End Class
Dim SuspendOriginal As Boolean
Protected Sub UpdateFilter()
Dim expressions As New List(Of Expression)
Dim expression As New Expression
Dim lastPos As Int32 = -1
Dim startPos As Int32 = 0
Dim nextVal As String = "P"
Dim IsString As Boolean
Dim foundBegining As Boolean
For i As Int32 = 0 To _filter.Length - 1
Select Case nextVal
Case "P"
If Not foundBegining AndAlso _filter(i) <> " " Then
foundBegining = True
startPos = i
ElseIf foundBegining Then
If _filter(i) = " "c Then
expression.PropertyName = _filter.Substring(startPos, i - startPos)
foundBegining = False
nextVal = "O"
ElseIf _filter(i) = "="c Then
foundBegining = False
expression.PropertyName = _filter.Substring(startPos, i - startPos)
expression.Condition = "="
nextVal = "C"
startPos = i
End If
End If
Case "O"
If _filter(i) = "=" Then
expression.Condition = "="
nextVal = "C"
startPos = i
End If
Case "C"
If Not foundBegining AndAlso _filter(i) <> " " Then
If _filter(i) = "'" Then IsString = True
foundBegining = True
startPos = i
ElseIf foundBegining Then
If (IsString And _filter(i) = "'") Or (Not IsString And _filter(i) = " ") Or (i = Filter.Length - 1) Then
If IsString Then
expression.Criteria = _filter.Substring(startPos, i - startPos).Trim("'"c)
Else
expression.Criteria = _filter.Substring(startPos, i - startPos)
End If
foundBegining = False
expressions.Add(expression)
expression = Nothing
startPos = i
nextVal = "J"
End If
End If
Case "J"
If _filter.Substring(i - 2, 3).ToUpper = "AND" Then
expression = New Expression
nextVal = "P"
End If
End Select
Next
If originalCollection.Count = 0 Then
originalCollection.AddRange(Me)
End If
Me.Clear()
Me.SuspendOriginal = True
Dim NoMatch As Boolean
For Each item As T In originalCollection
NoMatch = False
For Each entry As Expression In expressions
Dim propdesc As PropertyDescriptor = TypeDescriptor.GetProperties(GetType(T))(entry.PropertyName)
Dim value As Object = propdesc.GetValue(item)
Select Case entry.Condition
Case "="
If value.ToString <> entry.Criteria Then
NoMatch = True
Exit For
End If
End Select
Next
If Not NoMatch Then
Me.Add(item)
End If
Next
Me.SuspendOriginal = False
End Sub
-
What about impementing your filters as delegates? The idea is to create a dictionary of filters containing named filters. The filter string then would contain the name of the filter plus the required parameters. Something like this:
Public Class BindingListView(Of T) : Inherits BindingList(Of T)
Implements IBindingListView
Delegate Function FilterPredicate(ByVal item As T, ByVal params() As String) As Boolean
Public FilterDict As Dictionary(Of String, FilterPredicate)
Dim originalCollection As List(Of T) = New List(Of T)
Dim SuspendOriginal As Boolean
Protected Sub UpdateFilter()
Dim params() As String = _filter.Split(";")
Dim isMatch As FilterPredicate = FilterDict(params(0)) 'params(0) is name of filter
If originalCollection.Count = 0 Then
originalCollection.AddRange(Me)
End If
Me.Clear()
Me.SuspendOriginal = True
For Each item As T In originalCollection
If isMatch(item, params) Then
Me.Add(item)
End If
Next
Me.SuspendOriginal = False
End Sub
End Class
Then you would define a filter like this:
Function LastNameAndNotFirstName(ByVal c As Customer, ByVal params() As String) As Boolean
Return c.LastName = params(1) And c.FirstName <> params(2)
End Function
Dim x As BindingListView(Of Customer) = New ...
x.FilterDict.Add("LastNameAndNotFirstName", AddressOf LastNameAndNotFirstName)
Later you can use the filter like this:
x.Filter = "LastNameAndNotFirstName;Thomas;John"
This way you don't need to parse the filter function. -
Mister Olivier, any sample complete in VB.NET ?olivier said:What about impementing your filters as delegates? The idea is to create a dictionary of filters containing named filters. The filter string then would contain the name of the filter plus the required parameters. Something like this:
Public Class BindingListView(Of T) : Inherits BindingList(Of T)
Implements IBindingListView
Delegate Function FilterPredicate(ByVal item As T, ByVal params() As String) As Boolean
Public FilterDict As Dictionary(Of String, FilterPredicate)
Dim originalCollection As List(Of T) = New List(Of T)
Dim SuspendOriginal As Boolean
Protected Sub UpdateFilter()
Dim params() As String = _filter.Split(";")
Dim isMatch As FilterPredicate = FilterDict(params(0)) 'params(0) is name of filter
If originalCollection.Count = 0 Then
originalCollection.AddRange(Me)
End If
Me.Clear()
Me.SuspendOriginal = True
For Each item As T In originalCollection
If isMatch(item, params) Then
Me.Add(item)
End If
Next
Me.SuspendOriginal = False
End Sub
End Class
Then you would define a filter like this:
Function LastNameAndNotFirstName(ByVal c As Customer, ByVal params() As String) As Boolean
Return c.LastName = params(1) And c.FirstName <> params(2)
End Function
Dim x As BindingListView(Of Customer) = New ...
x.FilterDict.Add("LastNameAndNotFirstName", AddressOf LastNameAndNotFirstName)
Later you can use the filter like this:
x.Filter = "LastNameAndNotFirstName;Thomas;John"
This way you don't need to parse the filter function.
Thanks in advanded. -
Yes, I have just tried to create a working example of this without success.ae said:
Mister Olivier, any sample complete in VB.NET ?olivier said:*snip*
Thanks in advanded.
So, I too would love to see a working sample to try please Olivier.
Thanks
-
Yes, I have just tried to create a working example of this without success.
So, I too would love to see a working sample to try please Olivier.
Thanks -
Hi misters again,olivier said:What about impementing your filters as delegates? The idea is to create a dictionary of filters containing named filters. The filter string then would contain the name of the filter plus the required parameters. Something like this:
Public Class BindingListView(Of T) : Inherits BindingList(Of T)
Implements IBindingListView
Delegate Function FilterPredicate(ByVal item As T, ByVal params() As String) As Boolean
Public FilterDict As Dictionary(Of String, FilterPredicate)
Dim originalCollection As List(Of T) = New List(Of T)
Dim SuspendOriginal As Boolean
Protected Sub UpdateFilter()
Dim params() As String = _filter.Split(";")
Dim isMatch As FilterPredicate = FilterDict(params(0)) 'params(0) is name of filter
If originalCollection.Count = 0 Then
originalCollection.AddRange(Me)
End If
Me.Clear()
Me.SuspendOriginal = True
For Each item As T In originalCollection
If isMatch(item, params) Then
Me.Add(item)
End If
Next
Me.SuspendOriginal = False
End Sub
End Class
Then you would define a filter like this:
Function LastNameAndNotFirstName(ByVal c As Customer, ByVal params() As String) As Boolean
Return c.LastName = params(1) And c.FirstName <> params(2)
End Function
Dim x As BindingListView(Of Customer) = New ...
x.FilterDict.Add("LastNameAndNotFirstName", AddressOf LastNameAndNotFirstName)
Later you can use the filter like this:
x.Filter = "LastNameAndNotFirstName;Thomas;John"
This way you don't need to parse the filter function.
what is "_filter" ?? what type ?? I need full implementation of BindingListView Class and sample code, please !!!.
Thanks in advanced, Any help for me very grateful, Thank you very much !!! -
Why not just use dynamic linq and let it parse for you.
Example:
var q = db.Users.Where("UserID>1 and IsValid");
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx -
I use .NET 3.0 not .NET 3.5, thanks.staceyw said:Why not just use dynamic linq and let it parse for you.
Example:
var q = db.Users.Where("UserID>1 and IsValid");
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I need full implementation of BindingListView Class for Filter and sample code, please !!!.
-
ae said:
I use .NET 3.0 not .NET 3.5, thanks.staceyw said:*snip*
I need full implementation of BindingListView Class for Filter and sample code, please !!!.Sorry for the dumb question, but why not just download 3.5 and use. If you use 3.0 already, your almost there. Just a few more libraries. Is there a reason you can not use 3.5?
-
Not possible, my company and client wants .Net 3.0, and client pay the project.staceyw said:ae said:*snip*Sorry for the dumb question, but why not just download 3.5 and use. If you use 3.0 already, your almost there. Just a few more libraries. Is there a reason you can not use 3.5?
I need full implementation of BindingListView Class for Filter and sample code for .NET 3.0, please !!!.
Any help ?
-
This works although "rough and ready":
Public Class Form1
Private _BLV As BindingListView(Of Customer)
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
cboFirstName.SelectedIndex = 0
cboLastName.SelectedIndex = 0
_BLV = New BindingListView(Of Customer)
FillControls()
End Sub
Private Sub FillControls()
_BLV.Add(New Customer("Fred", "Smith"))
_BLV.Add(New Customer("Fred", "Jones"))
_BLV.Add(New Customer("Jack", "Jones"))
_BLV.Add(New Customer("Jack", "Pilchard"))
_BLV.Add(New Customer("Fred", "Pilchard"))
_BLV.Add(New Customer("Derek", "Pilchard"))
_BLV.Add(New Customer("Fred", "Smith"))
_BLV.Add(New Customer("Derek", "Smith"))
_BLV.Add(New Customer("Fred", "Smith"))
dgvCustomers.DataSource = _BLV
_BLV.FilterDict.Add("LastNameAndNotFirstName", AddressOf _BLV.LastNameAndNotFirstName)
End Sub
Private Sub cboFirstName_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboFirstName.SelectedIndexChanged
Filter()
End Sub
Private Sub cboLastName_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboLastName.SelectedIndexChanged
Filter()
End Sub
Private Sub Filter()
If cboFirstName.SelectedIndex <> 0 AndAlso cboLastName.SelectedIndex <> 0 Then
_BLV.Filter = String.Format("LastNameAndNotFirstName;{0};{1}", cboFirstName.SelectedItem.ToString(), cboLastName.SelectedItem.ToString())
End If
End Sub
End Class
Option Strict On
Imports System.ComponentModel
Public Class BindingListView(Of T) : Inherits BindingList(Of T)
Implements IBindingListView
Delegate Function FilterPredicate(ByVal item As T, ByVal params() As String) As Boolean
Public FilterDict As Dictionary(Of String, FilterPredicate) = New Dictionary(Of String, FilterPredicate)
Dim originalCollection As List(Of T) = New List(Of T)
Dim SuspendOriginal As Boolean
Private _Filter As String
Protected Sub UpdateFilter()
Dim params() As String = _Filter.Split(CChar(";"))
Dim isMatch As FilterPredicate = FilterDict(params(0)) 'params(0) is name of filter
If originalCollection.Count = 0 Then
originalCollection.AddRange(Me)
End If
Me.Clear()
Me.SuspendOriginal = True
For Each item As T In originalCollection
If isMatch(item, params) Then
Me.Add(item)
End If
Next
Me.SuspendOriginal = False
End Sub
Public Function LastNameAndNotFirstName(ByVal c As Customer, ByVal params() As String) As Boolean
Return c.FirstName = params(1) And c.LastName = params(2)
End Function
Public Sub ApplySort(ByVal sorts As System.ComponentModel.ListSortDescriptionCollection) Implements System.ComponentModel.IBindingListView.ApplySort
End Sub
Public Property Filter() As String Implements System.ComponentModel.IBindingListView.Filter
Get
Return _Filter
End Get
Set(ByVal value As String)
_Filter = value
UpdateFilter()
End Set
End Property
Public Sub RemoveFilter() Implements System.ComponentModel.IBindingListView.RemoveFilter
End Sub
Public ReadOnly Property SortDescriptions() As System.ComponentModel.ListSortDescriptionCollection Implements System.ComponentModel.IBindingListView.SortDescriptions
Get
Return Nothing
End Get
End Property
Public ReadOnly Property SupportsAdvancedSorting() As Boolean Implements System.ComponentModel.IBindingListView.SupportsAdvancedSorting
Get
End Get
End Property
Public ReadOnly Property SupportsFiltering() As Boolean Implements System.ComponentModel.IBindingListView.SupportsFiltering
Get
End Get
End Property
End Class
Public Class Customer
Private _FirstName As String
Private _LastName As String
Public Sub New(ByVal FName As String, ByVal LName As String)
_FirstName = FName
_LastName = LName
End Sub
Public ReadOnly Property FirstName() As String
Get
Return _FirstName
End Get
End Property
Public ReadOnly Property LastName() As String
Get
Return _LastName
End Get
End Property
End Class
Thread Closed
This thread is kinda stale and has been closed but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.