Источник привязки.Найдите несколько столбцов
-
21-09-2019 - |
Вопрос
Можно ли использовать метод Find BindingSource для нескольких столбцов?
Например, допустим, у меня есть gridview, отображающий текущих домашних животных;два выпадающих списка, cboPetType и cboGender;и кнопка для создания новой записи в таблице домашних животных на основе значений этих двух списков со списком.
Теперь, допустим, мне нужен только один из каждой комбинации типа животного / Пола (Собака - M, Кошка - F и т.д.).Итак, если у меня есть домашнее животное Dog - M в моем BindingSource и пользователь выбирает Dog и M из выпадающих списков, я хотел бы остановить пользователя, чтобы сообщить ему, что комбинация уже существует.
В прошлом я использовал BindingSource.Метод Find для выполнения чего-то подобного, но, насколько я могу судить, это подходит только для поиска по одному столбцу (т.Е.Источник привязки.Найти("petType", cboPetType.Выбранное значение);).
Можно ли выполнить поиск в bindingsource на основе нескольких столбцов?Если нет, то есть какие-либо предложения по достижению желаемого результата?Любой совет очень ценится!
Решение
Нет, к сожалению, это невозможно.Хотя вполне вероятно, что, учитывая конкретный источник данных, что такой поиск был бы довольно простым, выполняя его более общим способом (поскольку BindingSource
would) немного менее прозрачен.Во-первых, синтаксис был бы менее чем очевиден.Вот несколько надуманное решение:
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;
}
Вы можете назвать это так:
BindingSource source = // your BindingSource, obviously
int index = source.Find(
new Key { PropertyName = "PetType", Value = "Dog" },
new Key { PropertyName = "Gender", Value = "M" });
Имейте в виду, что для того, чтобы это было удобно, вы действительно нужен более умный алгоритм сравнения, но я оставлю это в качестве упражнения для читателя.Проверка реализации IComparable
было бы хорошим началом.Тем не менее, концепция должна быть реализована независимо от этого конкретного момента реализации.
Обратите внимание, что при этом не будут использованы преимущества какой-либо из возможных оптимизаций производительности, которые могут быть реализованы базовым источником данных, тогда как один столбец Find
бы.
Другие советы
Еще одно более простое решение, на случай, если кто-то столкнется с такой же проблемой.Это работает, когда BindingSource является 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
Это моя версия, основанная на приведенных выше примерах.Это работает очень хорошо.
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
Я называю это так:
e.Cancel = clsBSHelpers.Find(CastingBedBindingSource, _
New clsBSHelpers.Key("PlantID", m_PlantID), _
New clsBSHelpers.Key("LineBedNUmber", m_LineBedNumber))
Надеюсь, это поможет тем, кому нравится все просто.
Более простым решением является использование метода расширения:
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));