.net - pleno - ¿Cómo vincular un CheckBox a un bool tipeado DbColumn que es anulable?
nulidad de pleno derecho (2)
En Windows Forms (.NET 2.0, Visual Studio 2005 SP1): Tengo un DataSet
tipeado, con una columna cuyo tipo es System.Boolean
, que es anulable y cuyo valor predeterminado es DBNull
. Tengo un Form
contiene un control CheckBox
que quiero vincular al valor de la columna anterior.
- He intentado vincular la propiedad
Checked
a la columna a través del diseñador: funciona muy bien, solo si el valor predeterminado para la columna se establece enTrue
oFalse
. Intenté vincular la propiedad
CheckState
a la columna a través del diseñador y adjuntar mis propios manejadores de eventosFormat
yParse
, pero nunca se llaman:b.Format+=delegate(object sender, ConvertEventArgs cevent) { cevent.Value=DoFormat((CheckState)cevent.Value); // cf. end of the question }; b.Parse+=delegate(object sender, ConvertEventArgs cevent) { cevent.Value=DoParse(cevent.Value); // cf. end of the question };
He intentado crear una instancia de
Binding
personalizada en el código, adjuntar mis controladores de eventos y agregarlo a los enlaces deCheckBox
: los controladores de eventos nunca se llaman ...Binding b=new Binding("CheckState", _BindingSource, "MyColumn", false, DataSourceUpdateMode.OnPropertyChanged, DBNull.Value);
Como nota: un valor DBNull
es aceptable solo cuando proviene del DataSet
(significa que el valor nunca se ha configurado). Pero el usuario solo debería poder establecer el valor en True
o False
través del CheckBox
.
Como referencia, aquí está el código de los métodos de análisis y formateo:
internal static CheckState DoParse(object value)
{
if ((value==null) || (value is DBNull))
return CheckState.Indeterminate;
bool v=Convert.ToBoolean(value);
return (v ? CheckState.Checked : CheckState.Unchecked);
}
internal static object DoFormat(CheckState value)
{
switch (value)
{
case CheckState.Checked:
return true;
case CheckState.Indeterminate:
return DBNull.Value;
case CheckState.Unchecked:
return false;
}
return null;
}
La forma más fácil que conozco, se deriva de la clase CheckBox, agrega la propiedad "DataValue" que puede manejar los valores DBNull y vincular los datos a la propiedad "DataValue":
public class DataCheckBox : CheckBox {
public virtual object DataValue {
get { return this.Checked; }
set {
if ( value == null || value is DBNull ) {
this.CheckState = CheckState.Indeterminate;
}
else {
this.Checked = (bool)value;
}
}
}
}
¿Intentó vincular CheckBox.CheckState a DataColumn sin asociarlo a los eventos Parse and Format o al Messing?
Lamentablemente, no tengo una instancia de Visual Studio 2005 disponible, pero armé un formulario rápido en Visual Studio 2008 e hice exactamente lo que usted especificó:
Como nota: un valor DBNull es aceptable solo cuando proviene del DataSet (significa que el valor nunca se ha configurado). Pero el usuario solo debería poder establecer el valor en True o False a través del CheckBox.
Puedo ser el Parse, Format o Binding en su camino o puede ser que Windows Forms se comporte de manera diferente en 2008 que en 2005
ACTUALIZACIÓN 18 de agosto: Funciona en Visual Studio 2005 también a través del diseñador y del código. Aquí está el código que lo demuestra funcionando:
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
DataTable table = new DataTable();
public Form1() {
InitializeComponent();
//Creates the table structure
table.Columns.Add("Name", typeof(string));
table.Columns.Add("MyColumn", typeof(bool));
//Populates the table with some stuff
for (int i = 0; i < 5; i++) {
table.Rows.Add(i.ToString());
}
//Creates the controls and puts them on the form.
TextBox textBox = new TextBox();
textBox.Location = new Point(10, 10);
textBox.DataBindings.Add("Text", table, "Name");
CheckBox checkBox = new CheckBox();
checkBox.Left = textBox.Left;
checkBox.Top = textBox.Bottom + 10;
//Without true on the last argument, it will not work properly.
checkBox.DataBindings.Add("CheckState", table, "MyColumn", true);
Button previous = new Button();
previous.Text = "";
next.Top = previous.Top;
next.Left = previous.Right + 5;
next.Click += new EventHandler(next_Click);
this.Controls.AddRange(new Control[] { textBox, checkBox, previous, next });
}
void next_Click(object sender, EventArgs e) {
this.BindingContext[this.table].Position++;
}
void previous_Click(object sender, EventArgs e) {
this.BindingContext[this.table].Position--;
}
}
}
ACTUALIZACIÓN 23 de agosto:
Por qué funciona
La vinculación tiene un método privado llamado FormatObject que es responsable de obtener una representación del valor proveniente de la fuente de datos que es apropiado para mostrarse en el control.
Cuando se habilita el formateo, Binding.FormatObject () se ejecutará a través de una ruta de código que llamará a los manejadores eventuales que tenga para el evento Binding.Format. Si cualquier controlador cambia el valor que se propaga desde el origen de datos al control a través de ConvertEventArgs.Value, se usará ese valor. De lo contrario, llamará a un formateador predeterminado llamado FormatObject en una clase interna llamada System.Windows.Forms.Formatter.
Los comentarios sobre el estado del código fuente:
"El verdadero trabajo de conversión ocurre dentro de FormatObjectInternal ()"
Los comentarios para el estado FormatObjectInternal:
"Realiza algunas conversiones de casos especiales (por ejemplo, Boolean a CheckState)"
Dentro de FormatObjectInternal comprueba si el valor que proviene de la fuente de datos es nulo o DBNull y, si ese es el caso, comprueba si el tipo de propiedad que se enlaza es de CheckState. Si ese es el caso, devuelve CheckState.Indeterminate.
Como puede ver, este es un caso tan común que es una sorpresa que no funcionó en Windows Forms 1.x. Afortunadamente, lo arregló en 2.0 y más allá.