Ufa ! Essa deu trabalho, mas acabei descobrindo como resolver.
Me assustei por não encontrar exemplos ou referências sobre isso na internet, já que me pareceu um cenário até comum. Caso alguem já tenha passado por algo parecido, gostaria de algum comentário.
Cenário :
Uma combobox em windows form vinculada com um arrayList de itens para a exibição da lista e vinculada a uma origem de dados para gravação/leitura do item selecionado.
Os itens dentro do ArrayList precisam ser uma classe personalizada, para desta forma podermos ter o conjunto Texto/Valor e podermos ter o controle do formato de exibição dos itens na lista. Veja como fica essa classe personalizada a ser inserida dentro de um arrayList :
Public Class ItemLista
Dim vTexto As String
Dim vValor As String
Public Sub New(ByVal Texto As String, ByVal Valor As String)
vTexto = Texto
vValor = Valor
End Sub
Public Property Texto() As String
Get
Return (vTexto)
End Get
Set(ByVal Value As String)
vTexto = Value
End Set
End Property
Public Property Value() As String
Get
Return (vValor)
End Get
Set(ByVal Value As String)
vValor = Value
End Set
End Property
Public Function formatado() As String
Return (ToString())
End Function
Public Overrides Function toString() As String
Return (Value & " - " & Texto)
End Function
End Class
Vejam então o código de vinculo de uma combo :
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim obj As New ArrayList
obj.Add(New ItemLista("teste", 1))
obj.Add(New ItemLista("teste 2", 2))
obj.Add(New ItemLista("teste 3", 3))
ComboBox1.DisplayMember = "formato"
ComboBox1.ValueMember = "Value"
ComboBox1.DataSource = obj
End Sub
E a tentativa de vínculo desta combo com uma origem de dados :
combobox1.databindings.add("SelectedValue", dados.Tables(0), "CampoValorSelecionado")
Problema : Simplesmente não funciona. A combo não consegue exibir o valor do item que está no banco nem gravar corretamente o valor selecionado.
Documentação na internet : Segundo as documentações que localizei na internet, qualquer classe que implemente a interface IList pode ser vinculada a um objeto visual, tal como uma combobox. Conforme identifiquei com o exemplo acima, é apenas parcialmente verdade, pois funciona com limitações.
Limitações : Demorei algum tempo até descobrir, e finalmente veio-me a luz! :-) Veja esse código adicional, inserido em um botão :
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
ComboBox1.SelectedValue = 3
End Sub
Esse código não funciona quando a combobox está vinculada em um arrayList. Isso faz com que a combo vinculada em um arrayList não possa ter o selectedValue vinculado a um dataSet, pois não consegue fazer atribuições ao selectedValue.
Solução : Tentei com uma classe personalizada herdando de collectionBase, em vão. Até que finalmente fiz o óbvio : Comparar uma classe vinculável, como a dataView, com uma classe com esse problema, como o Arraylist, usando o object browser do visual studio. Bingo !
A classe dataView implementa a interface IBindingList, enquanto o arrayList não implementa essa interface. Essa é justamente a característica que causa essa diferença/problema.
Desta forma, apesar de ser dito que podemos fazer dataBinding com qualquer classe que implemente IList, só temos as funcionalidades completas se implementarmos IBindingList.
Criei uma classe personalizada implementando IBindingList. Muitos métodos da IBindingList podem ser implementados de forma dummy, ou seja, sem que realmente façam coisa alguma. O método mais importante, necessário para o processo de binding com a comboBox, é o método Find.
Veja mais abaixo como fica a classe implementando a IBindingList .
Uma das possibilidades para o fato destas características não terem sido notadas é que é possível criar uma dataTable e usa-la para substituir o ArrayList.
Public
Class colecao
Implements System.ComponentModel.IBindingList
Dim obj As New ArrayList
Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) Implements System.Collections.ICollection.CopyTo
obj.CopyTo(array, index)
End Sub
Public ReadOnly Property Count() As Integer Implements System.Collections.ICollection.Count
Get
Return (obj.Count)
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean Implements System.Collections.ICollection.IsSynchronized
Get
Return (obj.IsSynchronized)
End Get
End Property
Public ReadOnly Property SyncRoot() As Object Implements System.Collections.ICollection.SyncRoot
Get
Return (obj.SyncRoot)
End Get
End Property
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return (obj.GetEnumerator)
End Function
Public Function Add(ByVal value As Object) As Integer Implements System.Collections.IList.Add
obj.Add(value)
Return (obj.Count - 1)
End Function
Public Sub Clear() Implements System.Collections.IList.Clear
obj.Clear()
End Sub
Public Function Contains(ByVal value As Object) As Boolean Implements System.Collections.IList.Contains
Return (obj.Contains(value))
End Function
Public Function IndexOf(ByVal value As Object) As Integer Implements System.Collections.IList.IndexOf
Return (obj.IndexOf(value))
End Function
Public Sub Insert(ByVal index As Integer, ByVal value As Object) Implements System.Collections.IList.Insert
obj.Insert(index, value)
End Sub
Public ReadOnly Property IsFixedSize() As Boolean Implements System.Collections.IList.IsFixedSize
Get
Return (obj.IsFixedSize)
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.IList.IsReadOnly
Get
Return (obj.IsReadOnly)
End Get
End Property
Default Public Property Item(ByVal index As Integer) As Object Implements System.Collections.IList.Item
Get
Return (obj(index))
End Get
Set(ByVal Value As Object)
obj(index) = Value
End Set
End Property
Public Sub Remove(ByVal value As Object) Implements System.Collections.IList.Remove
obj.Remove(value)
End Sub
Public Sub RemoveAt(ByVal index As Integer) Implements System.Collections.IList.RemoveAt
obj.RemoveAt(index)
End Sub
'Falta
Public Sub AddIndex(ByVal [property] As System.ComponentModel.PropertyDescriptor) Implements System.ComponentModel.IBindingList.AddIndex
End Sub
'Falta
Public Function AddNew() As Object Implements System.ComponentModel.IBindingList.AddNew
End Function
'Falta
Public ReadOnly Property AllowEdit() As Boolean Implements System.ComponentModel.IBindingList.AllowEdit
Get
End Get
End Property
'Falta
Public ReadOnly Property AllowNew() As Boolean Implements System.ComponentModel.IBindingList.AllowNew
Get
End Get
End Property
'Falta
Public ReadOnly Property AllowRemove() As Boolean Implements System.ComponentModel.IBindingList.AllowRemove
Get
End Get
End Property
'Falta
Public Sub ApplySort(ByVal [property] As System.ComponentModel.PropertyDescriptor, ByVal direction As System.ComponentModel.ListSortDirection) Implements System.ComponentModel.IBindingList.ApplySort
End Sub
Public Function Find(ByVal [property] As System.ComponentModel.PropertyDescriptor, ByVal key As Object) As Integer Implements System.ComponentModel.IBindingList.Find
Dim o As Integer
For o = 0 To obj.Count - 1
If [property].GetValue(obj(o)) = key Then
Return (o)
End If
Next
Return (-1)
End Function
'Falta
Public ReadOnly Property IsSorted() As Boolean Implements System.ComponentModel.IBindingList.IsSorted
Get
End Get
End Property
Public Event ListChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ListChangedEventArgs) Implements System.ComponentModel.IBindingList.ListChanged
'Falta
Public Sub RemoveIndex(ByVal [property] As System.ComponentModel.PropertyDescriptor) Implements System.ComponentModel.IBindingList.RemoveIndex
End Sub
'Falta
Public Sub RemoveSort() Implements System.ComponentModel.IBindingList.RemoveSort
End Sub
'Falta
Public ReadOnly Property SortDirection() As System.ComponentModel.ListSortDirection Implements System.ComponentModel.IBindingList.SortDirection
Get
End Get
End Property
'Falta
Public ReadOnly Property SortProperty() As System.ComponentModel.PropertyDescriptor Implements System.ComponentModel.IBindingList.SortProperty
Get
End Get
End Property
'Falta
Public ReadOnly Property SupportsChangeNotification() As Boolean Implements System.ComponentModel.IBindingList.SupportsChangeNotification
Get
End Get
End Property
Public ReadOnly Property SupportsSearching() As Boolean Implements System.ComponentModel.IBindingList.SupportsSearching
Get
Return (True)
End Get
End Property
'Falta
Public ReadOnly Property SupportsSorting() As Boolean Implements System.ComponentModel.IBindingList.SupportsSorting
Get
End Get
End Property
End
Class