Enlazar bindingsource con un objeto propio

02/01/2009 - 16:13 por José Luis | Informe spam
Hola a todos

Tengo un componente que implementa IListSource y que, por lo tanto, puedo
enlazar en un BindingSource. Al implementar IListSource he hecho lo
siguiente:
- ContainsListCollection devuelve false

- GetList() devuelve un objeto que implementa IList.

Hasta aquí todo ok

Ahora he querido crear un objeto que contenga una lista de objetos
enlazables. Podíamos decir que mi objeto inicial es un datatable y el nuevo
un dataset.

El caso es que según la documentación de microsoft, esto es tan fácil
como hacer que ContainsListCollection devuelva true y que GetList devuelva
una lista de objetos que implementen IList.
Pues he hecho exactamente eso y cuando uso mi nuevo componente,
efectivamente puedo establecer el DataSource del BindingSource sin problemas
al componente, pero cuando despliego la propiedad DataMember, el diseñador
no me muestra nada.

¿Qué tengo que hacer para rellenar esta lista?


Gracias

Preguntas similare

Leer las respuestas

#1 José Luis
05/01/2009 - 09:33 | Informe spam
¿Alguna ayuda, por favor?
Respuesta Responder a este mensaje
#2 Jesús López
05/01/2009 - 11:19 | Informe spam
El caso es que según la documentación de microsoft, esto es tan fácil como
hacer que ContainsListCollection devuelva true y que GetList devuelva una
lista de objetos que implementen IList.



No es tan sencillo como dice la documentación. Para que te hagas una idea,
esto es más o menos lo que se hace para obtener la lista (en este caso un
IBindingList) dado el datasource y el datamember:

Aunque el código está en VB, supongo que te podrás hacer una idea:

Public Function GetList(ByVal DataSource As Object, ByVal DataMember As
String) As IBindingList
Dim listSource As IListSource = DirectCast(DataSource, IListSource)
Dim typeDescriptors As IList = listSource.GetList()
Dim typeDescriptor As ICustomTypeDescriptor DirectCast(typeDescriptors(0), ICustomTypeDescriptor)
Dim properties As PropertyDescriptorCollection typeDescriptor.GetProperties()
For Each propertyDescriptor As PropertyDescriptor In properties
Dim owner As Object typeDescriptor.GetPropertyOwner(propertyDescriptor)
If propertyDescriptor.Name = DataMember Then
Dim list As IBindingList DirectCast(propertyDescriptor.GetValue(owner), IBindingList)
Return list
End If
Next
Return Nothing
End Function



Efectivamente tu data source debe implementar IListSource, el
ContainsListCollection debe devolver true, y GetList tiene que devolver un
objeto que implementa IList, pero esa lista de objetos que devuleve GetList
es una lista con un único elemento, este elemento es un type descriptor. Un
type descriptor es un objeto a partir del cual se puede obtener información
de un tipo (una clase), esta información incluye, entre otras cosas, las
propiedades que tiene dicho tipo. Tu type descriptor tiene que estar
asociado al data source y en su método GetProperties debe devolver un
propertyDescriptor por cada datamember del data source asociado. Cada
propertyDescriptor que devulevas tiene que cumplir que su método GetValue te
devuelve un IList o un IBindingList.

Como ves, es un lío de cuidado.

En el caso de un DataSet, IListSource.GetList devuelve un DataViewManager.
El primer elemento del dataview manager es un
DataViewManagerListItemTypeDescriptor la cual implementa el interfaz
ICustomTypeDescriptor. El método GetProperties del
DataViewManagerListItemTypeDescriptor devuelve una colección de
DataTablePropertyDescriptor, la cual hereda de PropertyDescriptor, la
propiedad Name de DataTablePropertyDescriptor es el nombre del datatable y
el método GetValue de DataTablePropertyDescriptor devuelve el dataview
predeterminado del datatable. Como sabes, los dataviews implementan el
interfaz IBindingList.



Saludos:

Jesús López



"José Luis" escribió en el mensaje
news:
Hola a todos

Tengo un componente que implementa IListSource y que, por lo tanto, puedo
enlazar en un BindingSource. Al implementar IListSource he hecho lo
siguiente:
- ContainsListCollection devuelve false

- GetList() devuelve un objeto que implementa IList.

Hasta aquí todo ok

Ahora he querido crear un objeto que contenga una lista de objetos
enlazables. Podíamos decir que mi objeto inicial es un datatable y el
nuevo un dataset.

El caso es que según la documentación de microsoft, esto es tan fácil
como hacer que ContainsListCollection devuelva true y que GetList devuelva
una lista de objetos que implementen IList.
Pues he hecho exactamente eso y cuando uso mi nuevo componente,
efectivamente puedo establecer el DataSource del BindingSource sin
problemas al componente, pero cuando despliego la propiedad DataMember, el
diseñador no me muestra nada.

¿Qué tengo que hacer para rellenar esta lista?


Gracias
Respuesta Responder a este mensaje
#3 José Luis
05/01/2009 - 12:57 | Informe spam
Muchas gracias, Jesús.
Me pongo a trabajar.


"Jesús López" escribió en el
mensaje de noticias:
El caso es que según la documentación de microsoft, esto es tan fácil
como hacer que ContainsListCollection devuelva true y que GetList
devuelva una lista de objetos que implementen IList.



No es tan sencillo como dice la documentación. Para que te hagas una idea,
esto es más o menos lo que se hace para obtener la lista (en este caso un
IBindingList) dado el datasource y el datamember:

Aunque el código está en VB, supongo que te podrás hacer una idea:

Public Function GetList(ByVal DataSource As Object, ByVal DataMember As
String) As IBindingList
Dim listSource As IListSource = DirectCast(DataSource, IListSource)
Dim typeDescriptors As IList = listSource.GetList()
Dim typeDescriptor As ICustomTypeDescriptor > DirectCast(typeDescriptors(0), ICustomTypeDescriptor)
Dim properties As PropertyDescriptorCollection > typeDescriptor.GetProperties()
For Each propertyDescriptor As PropertyDescriptor In properties
Dim owner As Object > typeDescriptor.GetPropertyOwner(propertyDescriptor)
If propertyDescriptor.Name = DataMember Then
Dim list As IBindingList > DirectCast(propertyDescriptor.GetValue(owner), IBindingList)
Return list
End If
Next
Return Nothing
End Function



Efectivamente tu data source debe implementar IListSource, el
ContainsListCollection debe devolver true, y GetList tiene que devolver un
objeto que implementa IList, pero esa lista de objetos que devuleve
GetList es una lista con un único elemento, este elemento es un type
descriptor. Un type descriptor es un objeto a partir del cual se puede
obtener información de un tipo (una clase), esta información incluye,
entre otras cosas, las propiedades que tiene dicho tipo. Tu type
descriptor tiene que estar asociado al data source y en su método
GetProperties debe devolver un propertyDescriptor por cada datamember del
data source asociado. Cada propertyDescriptor que devulevas tiene que
cumplir que su método GetValue te devuelve un IList o un IBindingList.

Como ves, es un lío de cuidado.

En el caso de un DataSet, IListSource.GetList devuelve un DataViewManager.
El primer elemento del dataview manager es un
DataViewManagerListItemTypeDescriptor la cual implementa el interfaz
ICustomTypeDescriptor. El método GetProperties del
DataViewManagerListItemTypeDescriptor devuelve una colección de
DataTablePropertyDescriptor, la cual hereda de PropertyDescriptor, la
propiedad Name de DataTablePropertyDescriptor es el nombre del datatable y
el método GetValue de DataTablePropertyDescriptor devuelve el dataview
predeterminado del datatable. Como sabes, los dataviews implementan el
interfaz IBindingList.



Saludos:

Jesús López



"José Luis" escribió en el mensaje
news:
Hola a todos

Tengo un componente que implementa IListSource y que, por lo tanto, puedo
enlazar en un BindingSource. Al implementar IListSource he hecho lo
siguiente:
- ContainsListCollection devuelve false

- GetList() devuelve un objeto que implementa IList.

Hasta aquí todo ok

Ahora he querido crear un objeto que contenga una lista de objetos
enlazables. Podíamos decir que mi objeto inicial es un datatable y el
nuevo un dataset.

El caso es que según la documentación de microsoft, esto es tan fácil
como hacer que ContainsListCollection devuelva true y que GetList
devuelva una lista de objetos que implementen IList.
Pues he hecho exactamente eso y cuando uso mi nuevo componente,
efectivamente puedo establecer el DataSource del BindingSource sin
problemas al componente, pero cuando despliego la propiedad DataMember,
el diseñador no me muestra nada.

¿Qué tengo que hacer para rellenar esta lista?


Gracias




Respuesta Responder a este mensaje
#4 José Luis
08/01/2009 - 11:24 | Informe spam
O me paso o no llego:

Tras analizar tu respuesta y contrastarla con mi código vi que no estaba tan
lejos:

Ya tengo clases que implementan ICustomTypeDescriptor. Precisamente son
objetos de esta clase los que yo voy a meter dentro de mi lista.

Digamos que mi clase Tabla tiene la siguiente definición

public class TestTabla<TipoItem>: ITypedList, IBindingList, IListSource,
IList
{
...
}

Los items que contiene el objetoTabla son así:

public class ItemTabla: ICustomTypeDescriptor,IEditableObject,
INotifyPropertyChanged
{
...
}




Esto me funciona perfectamente. Puedo enlazar elementos TestTabla a
elementos de datos de Windows Forms sin problemas. Me he creado un
componente CmpTabla que encapsula una clase TestTabla y puedo usarlo en el
diseñador. Hasta aquí el resultado es bueno. No pongo la implementación
porque sería liar demasiado el tema. Cuando esto funcione, prometo subir un
ejemplo completo del desarrollo.

Siguiendo con el tema y releyendo tu post, me di cuenta que lo que me
faltaba para hacer que los elementos de la lista fueran accesibles desde el
diseñador, era hacer que la propia lista que uso para almacenar los objetos
fuera también una lista TestTabla. Creo una nueva clase: InfoTabla y las
clases necesarias alrededor de ella, para que la clase TestTabla no tenga
elementos ItemTabla, sino otra clase que sirva para describir objetos
TestTabla:


Los items que voy a guardar en la lista de tablas:

public class InfoTabla: ICustomTypeDescriptor{
public TestTabla<ItemTabla> Tabla = new TestTabla<ItemTabla>();

public override PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection pdc = new
PropertyDescriptorCollection(null);
TSDPropertyDescriptor Desc = new
TestPropertyDescriptor(Tabla.Nombre, Tabla);
pdc.Add(Desc);
return pdc;
}
...
}


Mi implementación de PropertyDescriptor:

public class TestPropertyDescriptor: PropertyDescriptor
{
public TSDPropertyDescriptor(string name, object value) : base(Name,
null)
{
...
}

public override object GetValue(object component)
{
InfoTabla Tabla = component as Tabla;
if(Tabla != null)
return Tabla.Tabla;
else
return null;
}
...
}


El componente para el diseñador:

public class TestDataset: Component, IListSource
{
TestTabla<InfoTabla> Lista = new TestTabla<InfoTabla>();

public bool ContainsListCollection
{
get { return true; }
}

public IList GetList()
{
if(Lista.Count == 0)
{
//Añado a la lista datos de ejemplo para tenerlos disponibles en
el diseñador
}
return Lista;
}
...
}



Termino los cambios, compilo la dll, la añado a un proyecto de prueba y...
¡¡funciona!! Como antes, puedo enlazar una instancia de mi componente
TesDataset a un DataGridView. Despliego el combo de "DataMember" y ... ¡¡
ahí tengo los datos de ejemplo con los que he rellenado la lista de tablas!!
Pero... ¿¿¿??? ¿Por qué puedo seguir desplegando el treeview??

La carga de datos de ejemplo en getlist, añade a la lista un item llamado
Eventos y otro llamado Localizaciones. El comportamiento que yo esperaba era
que en la propiedad DataMember me aparecieran estos dos elementos, y lo
hace, pero con un signo + a la izquierda. Al desplegar el elemento, veo que
dentro tiene una lista que contiene las dos tablas. No se si me explico,
algo tal que así:


DataSource: MiDataset
DataMember:
+Eventos
+Localizaciones

Al desplegar:



DataSource: MiDataset
DataMember:
- Eventos
Eventos
Localizaciones
- Localizaciones
Eventos
Localizaciones

Cuando selecciono un elemento de primer nivel, el datagridview no intenta
recuperar la lista de campos. Si selecciono uno de segundo nivel, el
diseñador me da el siguiente error: "El valor de la propiedad no es válido:
'La columna Eventos no pertenece a la tabla Eventos'".


Por otra parte, pruebo a enlazar el objeto MiDataset a un bindingsource y...
funciona. Cuando intento seleccionar la propiedad DataMember, la lista se me
muestra bien. Sólo veo Eventos y Localizaciones. Sin embargo, al decir que
el bindingsource sea el datasource del datagridview, el diseñador me muestra
el mismo error que os he puesto antes: "El valor de la propiedad no es
válido: 'La columna Eventos no pertenece a la tabla Eventos'".



Ya casi lo tengo, seguro, me falta algo que se me está escapando... ¿se os
ocurre qué me puede estar pasando? ¡¡Ayuda, please!!

Gracias por leer hasta aquí.
Respuesta Responder a este mensaje
#5 José Luis
09/01/2009 - 12:42 | Informe spam
Muchas gracias, Jesús. Examinaré el código que mandas.

Saludos

"Jesús" escribió en el mensaje de
noticias:
Sin tener el código completo es muy difícil averiguar todo lo que está
mal.
Así que lo que he hecho es una implementación de ejemplo que la puedes
encontrar en el archivo adjunto.

Saludos:

Jesús López

Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaSiguiente Respuesta Tengo una respuesta
Search Busqueda sugerida