BindingSource.Find Multiple Columns

Can I use the BindingSource search method for multiple columns?

For example, let's say I have a gridview displaying current pets; two comboboxes, cboPetType and cboGender; and a button to create a new record in the Pet table based on the values โ€‹โ€‹of these two combo boxes.

Now, let's say I only need one combination of PetType / Gender (Dog - M, Cat - F, etc.). So, if I have a Dog-M dog in my BindingSource and the user selects Dog and M from comboboxes, I would like to stop the user to let them know that this combination already exists.

In the past, I used the BindingSource.Find method to do something similar, but as far as I can tell, this is only useful for finding a single column (for example, BindingSource.Find ("PetType", cboPetType.SelectedValue) ;.)

Is it possible to search for a binding source based on multiple columns? If not, any suggestions to achieve the desired result? Any advice is appreciated!

+1
source share
4 answers

No, unfortunately, this is not possible. Although it is likely that using a particular data source will make this search quite simple, making it a more general way (since BindingSource ) will be slightly less transparent. First, the syntax will be less obvious. Here is a somewhat far-fetched solution:

 public class Key { public string PropertyName {get; set;} public object Value {get; set;} } public static int Find(this BindingSource source, params Key[] keys) { PropertyDescriptor[] properties = new PropertyDescriptor[keys.Length]; ITypedList typedList = source as ITypedList; if(source.Count <= 0) return -1; PropertyDescriptorCollection props; if(typedList != null) // obtain the PropertyDescriptors from the list { props = typedList.GetItemProperties(null); } else // use the TypeDescriptor on the first element of the list { props = TypeDescriptor.GetProperties(source[0]); } for(int i = 0; i < keys.Length; i++) { properties[i] = props.Find(keys[i].PropertyName, true, true); // will throw if the property isn't found } for(int i = 0; i < source.Count; i++) { object row = source[i]; bool match = true; for(int p = 0; p < keys.Count; p++) { if(properties[p].GetValue(row) != keys[p].Value)) { match = false; break; } } if(match) return i; } return -1; } 

You can call it like this:

 BindingSource source = // your BindingSource, obviously int index = source.Find( new Key { PropertyName = "PetType", Value = "Dog" }, new Key { PropertyName = "Gender", Value = "M" }); 

Keep in mind that in order for this to be useful, you really need a more reasonable comparison algorithm, but I will leave this as an exercise for the reader. Verifying the implementation of IComparable would be a good start. However, the concept should be implemented regardless of the specific point of implementation.

Note that this will not use any of the possible performance optimizations that can be implemented by the underlying data source, while a single Find column will be.

+3
source

Another simpler solution if someone runs into the same problem. This works when the BindingSource is a DataView:

 MyBindingSource.Sort = "Column1,Column2" Dim underlyingView As DataView = DirectCast(MyBindingSource.List, DataView) Dim searchVals As New List(Of Object) searchVals.Add("SearchString1") searchVals.Add("SearchString2") Dim ListIndex as Integer = underlyingView.Find(searchVals.ToArray) If ListIndex >=0 Then MyBindingList.Position = ListIndex Else 'No matches, so what you need to do... End If 
+2
source

This is my version based on the examples above. It works very well.

 Public Class clsBSHelpers Public Structure Key Public PropertyName As String Public Value As Object Sub New(ByVal pPropertyName As String, ByVal pValue As Object) PropertyName = pPropertyName Value = pValue End Sub End Structure Public Shared Function Find(ByVal Source As BindingSource, ByVal ParamArray keys As Key()) As Boolean Dim sb As New Text.StringBuilder For i As Integer = 0 To keys.Length - 1 If sb.Length > 0 Then sb.Append(",") End If sb.Append(keys(i).PropertyName) Next Source.Sort = sb.ToString Dim underlyingView As DataView = DirectCast(Source.List, DataView) Dim searchVals As New List(Of Object) For i As Integer = 0 To keys.Length - 1 searchVals.Add(keys(i).Value) Next Dim ListIndex As Integer = underlyingView.Find(searchVals.ToArray) If ListIndex >= 0 Then Source.Position = ListIndex Find = True Else Find = False 'No matches, so what you need to do... End If Return Find End Function End Class 

I call it this:

 e.Cancel = clsBSHelpers.Find(CastingBedBindingSource, _ New clsBSHelpers.Key("PlantID", m_PlantID), _ New clsBSHelpers.Key("LineBedNUmber", m_LineBedNumber)) 

Hope this helps those who like it.

0
source

A simpler solution is to use the extension method:

 var id1 = "id1"; var id2 = "id2"; var data = bindingSource1.Cast<DataModelType>().Single(r => r.ID1 == id1 && r.ID2 == id2); bindingSource1.Position = bindingSource1.IndexOf(data); 
0
source

All Articles