BindingSource.Find mehrere Spalten
-
21-09-2019 - |
Frage
Ist es möglich, die Methode Suchen eines Binding auf mehreren Spalten zu benutzen?
Zum Beispiel, sagen, ich habe ein gridview aktuelle Haustiere anzeigt; zwei Comboboxen, cboPetType und cboGender; und eine Schaltfläche, um einen neuen Datensatz in die Pet-Tabelle zu erstellen auf der Grundlage der Werte dieser beiden Comboboxen.
Nun lassen Sie uns sagen, ich will nur eine von jeder PetType / Geschlecht Kombination (Hund - M, Katze - F, etc.). Also, wenn ich einen Hund haben -. M Haustier in meiner Binding und wählt ein Benutzer Hund und M aus den Comboboxen, würde Ich mag den Benutzer zu stoppen, sie zu informieren, dass Kombination bereits vorhanden
In der Vergangenheit habe ich die BindingSource.Find Methode verwendet, um etwas ähnliches zu tun, aber, soweit ich das beurteilen kann, die für die Suche einer Spalte nur gut ist (dh BindingSource.Find ( „PetType“, cboPetType.SelectedValue );).
Ist es möglich, eine Binding basierend auf mehreren Spalten suchen? Wenn nicht, irgendwelche Vorschläge meine gewünschte Ergebnis zu erzielen? Jede Beratung ist sehr zu schätzen!
Lösung
Nein, das ist leider nicht möglich. Während es ist wahrscheinlich, da ein insbesondere Datenquelle, dass eine solche Suche ziemlich einfach sein würde, es in eine allgemeinere Art und Weise zu tun (wie die BindingSource
würde) etwas weniger transparent ist. Zum einen wäre die Syntax weniger als offensichtlich. Hier ist eine etwas gekünstelt Lösung:
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;
}
Sie können es so nennen:
BindingSource source = // your BindingSource, obviously
int index = source.Find(
new Key { PropertyName = "PetType", Value = "Dog" },
new Key { PropertyName = "Gender", Value = "M" });
Beachten Sie, dass für diese verwendbar zu sein, Sie wirklich brauchen einen intelligenteren Vergleichsalgorithmus, aber ich lasse das als eine Übung für den Leser. Überprüfen für eine Implementierung von IComparable
wäre ein guter Anfang sein. Dennoch sollte das Konzept durchsetzen, unabhängig von diesem speziellen Punkt der Umsetzung.
Beachten Sie, dass dies nicht von Vorteil jeder der möglichen Performance-Optimierungen stattfinden wird, die durch die zugrunde liegende Datenquelle implementiert werden könnten, während die einzelne Spalte Find
würde.
Andere Tipps
Eine andere einfachere Lösung, falls jemand läuft in der gleichen Ausgabe. Dies funktioniert, wenn der Binding ist ein 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
Dies ist meine Version auf die obigen Beispiele basieren. Es funktioniert sehr gut.
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
Ich nenne es so:
e.Cancel = clsBSHelpers.Find(CastingBedBindingSource, _
New clsBSHelpers.Key("PlantID", m_PlantID), _
New clsBSHelpers.Key("LineBedNUmber", m_LineBedNumber))
Hope dies hilft denen, die wie es einfach.
Die einfachere Lösung ist die Verwendung von Erweiterungsmethode:
var id1 = "id1";
var id2 = "id2";
var data = bindingSource1.Cast<DataModelType>().Single(r => r.ID1 == id1 && r.ID2 == id2);
bindingSource1.Position = bindingSource1.IndexOf(data);
var sorcobj = SorcBs.Current as Data.Student;
if (sorcobj == null) return;
TrgtBs.Position = TrgtBs.List.IndexOf(TrgtBs.List.OfType<Data.Student>().FirstOrDefault(s => s.NAME == sorc.NAME));