C# BindingSource。查找多个列

C# BindingSource。查找多个列,c#,winforms,find,bindingsource,multiple-columns,C#,Winforms,Find,Bindingsource,Multiple Columns,可以在多个列上使用BindingSource的Find方法吗 例如,假设我有一个显示当前宠物的gridview;两个组合框,cboPetType和cboGender;以及一个按钮,用于根据这两个组合框的值在Pet表中创建新记录 现在,让我们假设我只想要一个宠物类型/性别组合狗-M,猫-F,等等。。因此,如果我的BindingSource中有一只Dog-M宠物,并且用户从组合框中选择了Dog和M,我会阻止用户通知他们组合已经存在 在过去,我使用BindingSource.Find方法来执行类似的操

可以在多个列上使用BindingSource的Find方法吗

例如,假设我有一个显示当前宠物的gridview;两个组合框,cboPetType和cboGender;以及一个按钮,用于根据这两个组合框的值在Pet表中创建新记录

现在,让我们假设我只想要一个宠物类型/性别组合狗-M,猫-F,等等。。因此,如果我的BindingSource中有一只Dog-M宠物,并且用户从组合框中选择了Dog和M,我会阻止用户通知他们组合已经存在

在过去,我使用BindingSource.Find方法来执行类似的操作,但据我所知,这只适用于搜索一列,即BindingSource.FindPetType、cboPetType.SelectedValue


是否可以基于多个列搜索bindingsource?如果没有,有什么建议可以达到我想要的结果吗?非常感谢您的建议

不,不幸的是这是不可能的。虽然给定一个特定的数据源,这样的搜索可能会相当简单,但像BindingSource那样以更通用的方式进行搜索会有点不透明。首先,语法不太明显。下面是一个有点做作的解决方案:

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;
}
请记住,要使其可用,您确实需要一个更智能的比较算法,但我将把它作为练习留给读者。检查IComparable的实现将是一个良好的开端。尽管如此,无论具体的实施点是什么,这一概念都应该贯彻执行


请注意,这不会利用底层数据源可能实现的任何性能优化,而单列Find可以

不,不幸的是这是不可能的。虽然给定一个特定的数据源,这样的搜索可能会相当简单,但像BindingSource那样以更通用的方式进行搜索会有点不透明。首先,语法不太明显。下面是一个有点做作的解决方案:

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;
}
请记住,要使其可用,您确实需要一个更智能的比较算法,但我将把它作为练习留给读者。检查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

另一个更简单的解决方案,以防有人遇到同样的问题。当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))

希望这能帮助那些喜欢简单的人。

这是基于上述示例的我的版本。它工作得很好

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 id1 = "id1";
var id2 = "id2";

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

完美。唯一的更正是这一行是ifproperties[p].GetValuerow!=键[p]。值有一个需要删除的额外值。p效果。唯一的更正是这一行是ifproperties[p].GetValuerow!=键[p]。值有一个额外的值需要删除。请添加一些解释,以帮助用户理解为什么您认为您的答案可以解决此问题。谢谢。请添加一些解释,以帮助用户理解为什么您认为您的答案可以解决问题。非常感谢。