winforms - ejemplo - bindingsource definicion
Usando un BindingSource en un UserControl (4)
Tengo un UserControl con múltiples campos que me gustaría vincular a un BindingSource. También me gustaría que el UserControl exponga alguna propiedad de BindingSource para que se pueda descartar en un Formulario y vincularlo a BindingSource en el formulario. ¿Hay una forma fácil de hacer esto? Me doy cuenta de que puedo vincular todos los controles del UserControl en su setter BindSource. Pero esto parece estar mal. ¿Hay algún BindingSource Proxy que me permita vincular BindingSource en el control de usuario a BindingSource en el formulario?
Según su pregunta, apenas puedo obtener lo que pretende hacer. Por lo tanto, haré todo lo posible para proporcionarle, espero, información interesante sobre ese asunto.
Primero, consideremos el siguiente UserControl en un proyecto de software de administración de clientes.
public partial class CustomerManagementUserControl : UserControl {
public CustomerManagementUserControl() {
InitializeComponent();
_customerBindingSource = new BindingSource();
}
public IList<ICustomer> DataSource {
set {
_customerBindingSource.DataSource = value;
}
}
private BindingSource _customerBindingSource;
}
En segundo lugar, consideremos el siguiente Formulario, que debería ser su formulario de gestión de clientes.
public partial class CustomerManagementForm : Form {
public CustomerManagementForm() {
InitializeComponent();
_customerUserControl = new CustomerManagementUserControl();
_customerUserControl.Name = @"customerUserControl";
}
private void CustomerManagementForm_Load(object sender, EventArgs e) {
// CustomersFacade is simply a static class providing customer management features and requirements.
// Indeed, the GetCustomers() method shall return an IList<ICustomer>.
// The IList type and typed IList<T> are both intended to be bindable as a DataSource for DataBinding.
_customerUserControl.DataSource = CustomersFacade.GetCustomers();
this.Controls.Add(_customerUserControl);
}
private CustomerManagementUserControl _customerUserControl;
}
Si espera usar la propiedad CustomerManagementUserControl.DataSource desde la ventana de Propiedades, considere agregar lo siguiente sobre la definición de su propiedad.
[System.ComponentModel.DesignTimeVisible(true), System.ComponentModel.DesignerCategory("CustomerUserControl"), System.ComponentModel.Description("Sets the CustomerUserControl DataSource property")]
Esta es una forma de hacer lo que supongo que querrás hacer. Por otro lado, si lo que desea hacer es obtener lo más abstracto posible configurando un tipo diferente de objeto como su propiedad UserControl.BindingSource.DataSource, entonces tendrá que escribir un método que pueda detectar el tipo de objeto. el objeto pasado, luego vincula las propiedades en consecuencia. Una buena manera en que podría ir, tal vez, es Reflexión, si se siente cómodo trabajando con ella. De cualquier modo que pueda imaginar trabajando con tales características de polimorfismo, tendrá que escribir usted mismo una interfaz que todos sus objetos enlazables deberán implementar. De esta forma, evitará los nombres de propiedad desconocidos y cuándo llegará el momento de enlazar los controles de UserControl, podrá enlazar la propiedad correcta con el control correcto, y así sucesivamente.
Probemos lo siguiente:
public interface IEntity {
double Id { get; set; }
string Number { get; set; }
string Firstname { get; set; }
string Surname { get; set; }
long PhoneNumber { get; set; }
}
public interface ICustomer : IEntity {
}
public interface ISupplier : IEntity {
string Term { get; set; }
}
public sealed Customer : ICustomer {
public Customer() {
}
public double Id { get; set; }
public string Number { get; set; }
public string Firstname { get; set; }
public string Surname { get; set; }
public long PhoneNumber { get; set; }
}
public sealed Supplier : ISupplier {
public Supplier() {
}
public double Id { get; set; }
public string Number { get; set; }
public string Firstname { get; set; }
public string Surname { get; set; }
public long PhoneNumber { get; set; }
public string Term { get; set; }
}
Teniendo en cuenta el código anterior, puede usar la propiedad DataSource de su UserControl para enlazar con un IEntity, por lo que su propiedad podría así.
[System.ComponentModel.DesignTimeVisible(true), System.ComponentModel.DesignerCategory("CustomerUserControl"), System.ComponentModel.Description("Sets the CustomerUserControl DataSource property")]
public IList<IEntity> DataSource {
set {
_customerBindingSource.DataSource = value;
}
}
Dicho esto, si desea avanzar aún más, podría exponer las propiedades de DataBindings de control de UserControl para configurarlas en tiempo de diseño. Teniendo esto en cuenta, querrá exponer su BindingSource como una propiedad pública para que también pueda configurarlo en tiempo de diseño, luego elija su DataMember de este BindinSource.
Espero que esto les ayude a ambos un poco o al menos, darle algunas pistas para nuevas búsquedas.
Si quisiera hacer esto automáticamente, podría buscar el origen de enlace del formulario principal en el evento de carga de su control de usuario o algo así ...
Dim components As Reflection.FieldInfo = typ.GetField("components", Reflection.BindingFlags.DeclaredOnly Or Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
Dim lstBindingSources As New List(Of BindingSource)
For Each obj As Object In components.Components
Dim bindSource As BindingSource = TryCast(obj, BindingSource)
If bindSource IsNot Nothing Then
lstBindingSources.Add(bindSource)
End If
Next
If lstBindingSources.Count = 1 Then
MyBindingSource.DataSource = lstBindingSources(0).DataSource
End If
Si asigna la misma referencia de objeto que el origen de datos en dos fuentes de enlace, los controles no se actualizarán de forma coherente en el segundo origen de enlace. Posiblemente, un compromiso con las opciones anteriores es el siguiente:
- Agregue temporalmente un origen de enlace al control de usuario y use el diseñador de
VS
para establecer los enlaces a los controles. - traiga el
designer.vb
al editor de código. Busque todas las líneas"DataBindings.Add"
que fueron creadas por el diseñador. Cópialos todos en el bloc de notas. - elimine el bindingsource del diseñador y agregue una referencia de bindingsource en el código. Agregue una propiedad para el bindingsource con el mismo nombre que se utilizó en el diseñador. En el colocador de la propiedad, pegue todas las líneas del bloc de notas de arriba en el paso 2.
- En el evento Load del formulario, asigne el bindingsource del formulario a la propiedad en el control del usuario. Si el control del usuario está incrustado en otro control de usuario, puede usar el evento del control padre generado a mano para hacer lo mismo.
Hay menos tipeo y menos errores tipográficos porque el diseñador VS
está creando todos esos nombres de propiedad de texto literal.
Sé que es una respuesta tardía; sin embargo, podría ser útil para alguien más leyendo esta publicación.
Tengo controles en un UserControl
que están vinculados a datos. Necesito tener un BindingSource
en el UserControl
para poder vincular los controles en el momento del diseño. El "verdadero" BindingSource
, sin embargo, se asienta en el Form
. En otras palabras, los controles en UserControl
deben comportarse como si estuvieran sentados directamente en el formulario (o en un ContainerControl
en el formulario).
La idea detrás de esta solución es observar el evento DataSourceChanged
del BindingSource
"real" y asignar su DataSource
al BindingSource
local cuando cambie. Para encontrar el BindingSource
"real" BindingSource
que el Form
(o Control
) que lo contiene implemente la siguiente interfaz:
public interface IDataBound
{
BindingSource BindingSource { get; }
}
Podemos observar el evento ParentChanged
de un control para saber cuándo se ha agregado a un Form
o un ContainerControl
. El problema aquí es que este ContainerControl
podría no haberse agregado al Form
(u otro ContainerControl
) aún en este momento. En este caso, nos suscribimos al evento ParentChanged
del último padre que encontramos en la cadena de padres y esperamos hasta que este último padre haya sido agregado, y así sucesivamente, hasta que encontremos un Control
o Form
implementa IDataBound
. Cuando se ha encontrado un IDataBound
, nos suscribimos al evento DataSourceChanged
de su BindingSource
.
public partial class MyUserControl : UserControl
{
private IDataBound _dataBoundControl;
private Control _parent;
public MyUserControl()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) {
_parent = this;
SearchBindingSource();
}
}
private void SearchBindingSource()
{
if (_parent != null && _dataBoundControl == null) {
while (_parent.Parent != null) {
_parent = _parent.Parent;
_dataBoundControl = _parent as IDataBound;
if (_dataBoundControl != null) {
if (_dataBoundControl.BindingSource != null) {
_dataBoundControl.BindingSource.DataSourceChanged +=
new EventHandler(DataBoundControl_DataSourceChanged);
}
return;
}
}
// This control or one of its parents has not yet been added to a
// container. Watch for its ParentChanged event.
_parent.ParentChanged += new EventHandler(Parent_ParentChanged);
}
}
void Parent_ParentChanged(object sender, EventArgs e)
{
SearchBindingSource();
}
void DataBoundControl_DataSourceChanged(object sender, EventArgs e)
{
localBindingSource.DataSource = _dataBoundControl.BindingSource.DataSource;
}
}