Bindingsource.Contar várias colunas
-
21-09-2019 - |
Pergunta
É possível usar o método de localização de um BindingSource em várias colunas?
Por exemplo, digamos que eu tenha uma GridView exibindo animais de estimação atuais; dois combosbóxos, CbopeTtype e CBOGENDER; e um botão para criar um novo registro na tabela de animais de estimação com base nos valores desses dois combustos.
Agora, digamos que eu só quero uma de cada combinação de pettype/gênero (cachorro - M, Cat - F, etc.). Então, se eu tiver um cachorro - M PET no meu BindingSource e um usuário seleciona o cachorro e M das ComboBoxes, gostaria de impedir o usuário para informá -los de que a combinação já existe.
No passado, usei o método BindingSource.Find para fazer algo semelhante, mas, até onde eu sei, isso é bom apenas para pesquisar uma coluna (ou seja, Bindingsource.find ("Pettype", CbopeTtype.SelectectedValue);) .
É possível pesquisar um BindingSource com base em várias colunas? Caso contrário, alguma sugestão para alcançar o resultado desejado? Qualquer conselho é muito apreciado!
Solução
Não, infelizmente isso não é possível. Embora seja provável que dado um especial fonte de dados que essa pesquisa seria bastante simples, fazendo isso de uma maneira mais genérica (como o BindingSource
seria) é um pouco menos transparente. Por um lado, a sintaxe seria menos do que óbvia. Aqui está uma solução um tanto artificial:
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;
}
Você pode chamá -lo assim:
BindingSource source = // your BindingSource, obviously
int index = source.Find(
new Key { PropertyName = "PetType", Value = "Dog" },
new Key { PropertyName = "Gender", Value = "M" });
Tenha em mente que, para que isso seja utilizável, você verdade Precisa de um algoritmo de comparação mais inteligente, mas vou deixar isso como um exercício para o leitor. Verificando uma implementação de IComparable
seria um bom começo. No entanto, o conceito deve ser realizado independentemente desse ponto de implementação específico.
Observe que isso não aproveitará nenhuma das possíveis otimizações de desempenho que podem ser implementadas pela fonte de dados subjacente, enquanto a coluna única Find
gostaria.
Outras dicas
Outra solução mais simples, caso alguém encontre o mesmo problema. Isso funciona quando o BindingSource é um 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
Esta é a minha versão com base nos exemplos acima. Funciona muito bem.
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
Eu chamo assim:
e.Cancel = clsBSHelpers.Find(CastingBedBindingSource, _
New clsBSHelpers.Key("PlantID", m_PlantID), _
New clsBSHelpers.Key("LineBedNUmber", m_LineBedNumber))
Espero que isso ajude aqueles que gostam simples.
A solução mais simples é usando o método de extensão:
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));