In Windows Forms (.NET 2.0, Visual Studio 2005 SP1) : I have a typed DataSet
, with a column which type is System.Boolean
, which is nullable and whi
Have you tried binding CheckBox.CheckState to the DataColumn without attaching to Parse and Format events or messing with the Binding?
Unfortunately I don’t have an instance of Visual Studio 2005 available but I assembled a quick form in Visual Studio 2008 and it did exactly what you specified:
As a note : a DBNull value is acceptable only when coming from the DataSet (it means the value has never been set). But the user should only be able to set the value to True or False via the CheckBox.
I may be the Parse, Format or Binding getting in your way or it may be that Windows Forms behaves differently in 2008 than in 2005
UPDATE Aug 18: It works on Visual Studio 2005 too both through the designer and through code. Here's the code that demonstrates it working:
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--;
}
}
}
UPDATE Aug 23:
Why it works
Binding has a private method called FormatObject which is responsible for obtaining a representation of the value coming from the data source that is appropriate for being shown on the control.
When formatting is enabled, Binding.FormatObject() will run through a code path that will call eventual handlers you have for the Binding.Format event. If any handler changes the value being propagated from the data source to the control through ConvertEventArgs.Value, that value will be used. Otherwise, it will call a default formatter called FormatObject on an internal class called System.Windows.Forms.Formatter.
The comments on the source code state:
“The real conversion work happens inside FormatObjectInternal()”
The comments for FormatObjectInternal state:
“Performs some special-case conversions (eg. Boolean to CheckState)”
Inside of FormatObjectInternal it checks to see if the value coming from the data source is null or DBNull and if that is the case, it checks to see if the type of the property being bound is of CheckState. If that is the case, it returns CheckState.Indeterminate.
As you can see, this is such a common case that it is a surprise it didn't work on Windows Forms 1.x. Luckily, the fixed it on 2.0 and beyond.
The easist way I know, is derive from CheckBox class, add "DataValue" property which can handle DBNull values and bind the data to "DataValue" property:
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;
}
}
}
}