Hi Brian,
Unfortunately I don't know of any articles about the topic, however I am planning on writing one at some point. For now to get you started, here's a sample of a "NullableCheckBoxField". It allows editing of fields that are of type Nullable<bool> and actually supports all three states: true, false, null (not set). This is unlike the built-in GridView CheckBoxField that only supports true and false.
Here's the snippet of code:
namespace NullableTest {
using System;
using System.ComponentModel;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
public class NullableCheckBoxField : BoundField {
private PropertyDescriptor _boundFieldDesc;
// Called when a data bound control needs to get the value back out of the control(s) created by this cell
public override void ExtractValuesFromCell(System.Collections.Specialized.IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly) {
Control childControl = null;
string dataField = DataField;
object value = null;
if (cell.Controls.Count > 0) {
childControl = cell.Controls[0];
NullableCheckBox dropDownList = childControl as NullableCheckBox;
if (dropDownList != null) {
if (includeReadOnly || dropDownList.Enabled) {
switch (dropDownList.SelectedIndex) {
case 0:
value = new bool?(true);
break;
case 1:
value = new bool?(false);
break;
case 2:
value = null;
break;
}
}
}
}
if (dictionary.Contains(dataField)) {
dictionary[dataField] = value;
}
else {
dictionary.Add(dataField, value);
}
}
// Called when a data bound control need to get the value for this cell when is is being data bound
protected virtual object GetValue(Control controlContainer) {
object data = null;
object dataItem = null;
string boundField = DataField;
if (controlContainer == null) {
throw new ArgumentNullException("controlContainer");
}
// Get the DataItem from the container
dataItem = DataBinder.GetDataItem(controlContainer);
if (dataItem == null && !DesignMode) {
throw new InvalidOperationException("No data item");
}
// Get value of field in data item
if (_boundFieldDesc == null) {
if (!boundField.Equals(ThisBLOCKED EXPRESSION {
_boundFieldDesc = TypeDescriptor.GetProperties(dataItem).Find(boundField, true);
if ((_boundFieldDesc == null) && !DesignMode) {
throw new InvalidOperationException("The field '" + boundField + "' was not found on the data item");
}
}
}
if (_boundFieldDesc != null && dataItem != null) {
data = _boundFieldDesc.GetValue(dataItem);
}
else {
if (DesignMode) {
data = GetDesignTimeValue();
}
else {
data = dataItem;
}
}
return data;
}
// Called when the child controls for this cell need to be created
protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState) {
NullableCheckBox childControl = null;
NullableCheckBox boundControl = null;
if (((rowState & DataControlRowState.Edit) != 0 && ReadOnly == false) || (rowState & DataControlRowState.Insert) != 0) {
NullableCheckBox editor = new NullableCheckBox();
editor.ToolTip = HeaderText;
childControl = editor;
if (DataField.Length != 0 && (rowState & DataControlRowState.Edit) != 0) {
boundControl = editor;
}
}
else if (DataField.Length != 0) {
NullableCheckBox editor = new NullableCheckBox();
editor.Enabled = false;
childControl = editor;
boundControl = editor;
}
if (childControl != null) {
MethodInfo mi = typeof(Control).GetMethod("ClearChildState", (BindingFlags)(-1));
mi.Invoke(childControl, new object[0]);
cell.Controls.Add(childControl);
}
if (boundControl != null) {
boundControl.DataBinding += new EventHandler(OnDataBindField);
}
}
// Called when the cell is being data bound
protected override void OnDataBindField(object sender, EventArgs e) {
Control boundControl = (Control)sender;
Control controlContainer = boundControl.NamingContainer;
object data = GetValue(controlContainer);
bool? dataBool = (bool?)data;
if (!dataBool.HasValue) {
((NullableCheckBox)boundControl).SelectedIndex = 2;
}
else {
((NullableCheckBox)boundControl).SelectedIndex = dataBool.Value ? 0 : 1;
}
}
// Simple 3-state checkbox. Since no such thing exists in HTML, we use a DropDownList with three options instead.
private sealed class NullableCheckBox : DropDownList {
public NullableCheckBox() {
Items.Add("true");
Items.Add("false");
Items.Add("not set");
}
}
}
}
And you'd use it just like any other field in a GridView, except that it only works against Nullable<bool> types.
I hope this gets you pointed in the right direction!
Thanks,
Eilon