Вопрос

Можно ли использовать метод 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));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top