如何将 CheckBox 绑定到可为空的布尔类型 DbColumn?

Posted

技术标签:

【中文标题】如何将 CheckBox 绑定到可为空的布尔类型 DbColumn?【英文标题】:How to bind a CheckBox to a bool typed DbColumn that is nullable? 【发布时间】:2010-11-19 07:35:43 【问题描述】:

在 Windows 窗体(.NET 2.0,Visual Studio 2005 SP1)中:我有一个类型为 DataSet 的列,其类型为 System.Boolean,它可以为空,默认值为 DBNull。我有一个Form,其中包含一个我想绑定到前一列值的CheckBox 控件。

我尝试通过设计器将Checked 属性绑定到列:效果很好,只有当列的默认值设置为TrueFalse

我尝试通过设计器将CheckState 属性绑定到列,并附加我自己的FormatParse 事件处理程序,但它们从未被调用:

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
;

我尝试在代码中创建自定义 Binding 实例,附加我的事件处理程序并将其添加到 CheckBox 绑定:事件处理程序仍然永远不会被调用...

Binding b=new Binding("CheckState", _BindingSource, "MyColumn", false, DataSourceUpdateMode.OnPropertyChanged, DBNull.Value);

注意:DBNull 值仅在来自DataSet 时才可接受(这意味着该值从未设置过)。但用户应该只能通过CheckBox 将值设置为TrueFalse

供参考,这里是解析和格式化方法的代码:

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;

【问题讨论】:

我已在答案末尾添加了有关 *为什么会起作用 的信息。 【参考方案1】:

您是否尝试过将 CheckBox.CheckState 绑定到 DataColumn 而不附加到 Parse 和 Format 事件或弄乱绑定?

很遗憾,我没有可用的 Visual Studio 2005 实例,但我在 Visual Studio 2008 中组装了一个快速表单,它完全按照您指定的方式完成:

注意:DBNull 值仅在来自 DataSet 时才可接受(这意味着该值从未设置过)。但是用户应该只能通过 CheckBox 将值设置为 True 或 False。

我可能是 Parse、Format 或 Binding 妨碍了你,也可能是 Windows 窗体在 2008 年的行为与 2005 年不同


8 月 18 日更新: 它也可以通过设计器和代码在 Visual Studio 2005 上运行。 这是演示它工作的代码:


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 &lt 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--;
        
    



8 月 23 日更新:

为什么会起作用

Binding 有一个名为 FormatObject 的私有方法,它负责获取来自数据源的值的表示,该表示适合在控件上显示。

启用格式化后,Binding.FormatObject() 将通过代码路径运行,该代码路径将调用您为 Binding.Format 事件拥有的最终处理程序。如果任何处理程序更改了通过 ConvertEventArgs.Value 从数据源传播到控件的值,则将使用该值。否则,它将在名为 System.Windows.Forms.Formatter 的内部类上调用名为 FormatObject 的默认格式化程序。

源代码状态上的cmets:

“真正的转换工作发生在 FormatObjectInternal() 内部”

FormatObjectInternal 状态的 cmets:

“执行一些特殊情况的转换(例如,布尔到 CheckState)”

在 FormatObjectInternal 内部,它检查来自数据源的值是 null 还是 DBNull,如果是这种情况,它检查被绑定的属性的类型是否为 CheckState。如果是这种情况,则返回 CheckState.Indeterminate。

如您所见,这是一种常见的情况,令人惊讶的是它在 Windows Forms 1.x 上不起作用。幸运的是,它已在 2.0 及更高版本上修复。

【讨论】:

我认为将布尔数据绑定到枚举类型(有 3 个可能的值!)会伤害我的头(很多),但出于好奇,我无论如何都会尝试...跨度> 我在 Visual Studio 2005 上试过,它也可以。通过设计器和代码。 您的示例有效,但对于这个问题不正确。 @Mac 想要“DBNull 可以从行填充到控件,但不能从控件填充到行”。 “MyColumn”和“CheckState”之间的数据绑定没有实现它。要获得正确的行为,您需要挂钩 Format/Parse 事件并编辑传递值。 - 如果是@Max经常写的场景,最好使用一些可重用的解决方案。 我认为你们需要冷静一下...@Alfred Myers:感谢您的回答。由于个人喜好,我不会使用它(对我来说看起来太老套了),但是当我发现它为什么有效(当我认为它不应该)时,我肯定会学到很多东西。 @TcKs:他明确指出,不允许用户从控件将值设置为 DBNull:“只有来自 DataSet 时才可以接受 DBNull 值(这意味着值从未设置过)。但用户应该只能通过 CheckBox 将值设置为 True 或 False。"【参考方案2】:

我知道的最简单的方法是从 CheckBox 类派生,添加可以处理 DBNull 值的“DataValue”属性并将数据绑定到“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;
            
        
    

【讨论】:

聪明但并不真正需要,因为 CheckBox 支持数据绑定到可空 (DBNull) 列,正如我所演示的那样。

以上是关于如何将 CheckBox 绑定到可为空的布尔类型 DbColumn?的主要内容,如果未能解决你的问题,请参考以下文章

如何从大量可为空的布尔值中返回布尔值?

检查可为空的布尔值是不是为空[重复]

堆栈溢出错误可为空的数字类型c#

如何绑定可为空的十进制值

为啥我不能将 DBNull.Value 插入到 sql server 2005 中的可为空的图像字段中?

如何将 C# 可为空的 int 转换为 int