C#DateTimePicker DataBinding Parse事件无法正常工作

时间:2017-03-23 12:47:13

标签: c# parsing binding datetimepicker nullable

我有一个datetimepicker,我在数据集中绑定了可以为空的Date / Time列。我成功地将Format事件应用于null而不是null对象值。但是,当我取消选中dtp控件时,它不会在数据集中设置为null。 这是我的代码:

dtpBirthdate.DataBindings.Add(new Binding("Value", bsStaff, "birthDate", true));
dtpBirthdate.DataBindings["Value"].Format += new ConvertEventHandler(dtpFormat);
dtpBirthdate.DataBindings["Value"].Parse += new ConvertEventHandler(dtpParse);

格式化和解析事件:

private void dtpFormat(object sender, ConvertEventArgs e)
{
      Binding b = sender as Binding;
      if(b != null)
      {
           DateTimePicker dtp = (b.Control as DateTimePicker);
           if(dtp != null)
           {
                if (e.Value == null || e.Value == DBNull.Value)
                {
                    dtp.Checked = false;
                    dtp.CustomFormat = " ";
                    e.Value = false;
                }
                else
                {
                    dtp.Checked = true;
                    dtp.CustomFormat = "dd-MMM-yyyy";
                    dtp.Value = (DateTime) e.Value;
                }
            }
        }
    }

    private void dtpParse(object sender, ConvertEventArgs e)
    { 
        Binding b = sender as Binding;

        if (b != null)
        {
            DateTimePicker dtp = (b.Control as DateTimePicker);
            if (dtp != null)
            {
                if (dtp.Checked == false)
                {
                    e.Value = DBNull.Value;
                }
                else
                {
                    e.Value = dtp.Value; 
                }
            }
        }
   }

调试后,我发现它在解析和格式事件之间进行无限循环。我的代码出了什么问题?

编辑:还有一个绑定到bsStaff bindingsource的datagridview。

5 个答案:

答案 0 :(得分:2)

以下内容应解决问题(请参阅代码中的注释):

private void dtpFormat(object sender, ConvertEventArgs e)
{
    Binding b = sender as Binding;
    if(b != null)
    {
        DateTimePicker dtp = (b.Control as DateTimePicker);
        if (dtp != null)
        {
            if (e.Value == null || e.Value == DBNull.Value)
            {
                dtp.Checked = false;
                dtp.CustomFormat = " ";
                // e.Value = false;
                // To prevent dtp.Value property setter setting Checked back to true
                e.Value = dtp.Value; 
            }
            else
            {
                dtp.Checked = true;
                dtp.CustomFormat = "dd-MMM-yyyy";
                //dtp.Value = (DateTime) e.Value;                    
                // dtp.Value will be set to e.Value from databinding anyway
            }
        }
    }
}

private void dtpParse(object sender, ConvertEventArgs e)
{ 
    Binding b = sender as Binding;

    if (b != null)
    {
        DateTimePicker dtp = (b.Control as DateTimePicker);
        if (dtp != null)
        {
            if (dtp.Checked == false)
            {
                e.Value = DBNull.Value;
            }
            else
            {
                //e.Value = dtp.Value;
                // Do nothing, e.Value is already populated with dtp.Value
            }
        }
    }
}

但是整个想法从一开始就是错误的,因为它基于数据绑定基础架构攻击(典型的XY问题 - 克服DTP中缺少DateTime?值属性)。 ConvertParse事件应该执行从数据源值到控制值的转换,反之亦然。它们不应该读取或写入控制属性(它打破了整个封装),信息通过e.Valuee.DesiredType提供,处理程序应该基于此更改e.Value信息。

正确的方法是创建继承DateTimePicker并实现(shadow)DateTime? Value属性的自定义控件。属性getter和setter可以应用必要的逻辑(允许它们读取/修改其他属性)。然后用该自定义控件替换DTP控件,并简单地绑定到" Value"没有任何绑定事件处理程序的属性。

更新:这是一个快速而肮脏的非绑定方法实现:

public class CustomDateTimePicker : DateTimePicker
{
    public CustomDateTimePicker()
    {
        Format = DateTimePickerFormat.Custom;
        SetValueCore(null);
    }

    new public DateTime? Value
    {
        get { return Checked ? base.Value : (DateTime?)null; }
        set
        {
            if (Value != value)
                SetValueCore(value);
        }
    }

    private void SetValueCore(DateTime? value)
    {
        if (value == null)
            Checked = false;
        else
            base.Value = value.Value;
        UpdateCustomFormat();
    }

    protected override void OnValueChanged(EventArgs eventargs)
    {
        UpdateCustomFormat();
        base.OnValueChanged(eventargs);
    }

    private void UpdateCustomFormat()
    {
        CustomFormat = Value != null ? "dd-MMM-yyyy" : " ";
    }
}

答案 1 :(得分:1)

在null检查之前,您正在“绑定b = sender as Binding”。在投票前检查发件人是否为空,你应该没事。

答案 2 :(得分:1)

我注意到您正在为两个控件使用数据绑定事件捕获,但是在第一个dtpFormat事件处理程序中,您不首先检查数据绑定值。

Imho这行代码:

if (e.Value == null || e.Value == DBNull.Value)

需要更改为

if (e.Value == DBNull.Value || e.Value == null)

答案 3 :(得分:1)

问题是你需要将e.Value设置为某种东西;但如果你改变它,它将再次解析。尝试将其设置为原始值。

e.Value = dtp.Value;

这是一个链接到之前遇到此问题的人。他们没有使用你的DbNull.Value,但除此之外它几乎与你正在做的相同。

http://blogs.interknowlogy.com/2007/01/21/winforms-databinding-datetimepicker-to-a-nullable-type/

答案 4 :(得分:0)

dtpParse正在设置e.Value = dbNull.Value,它会在值发生变化时触发dtpFormat,然后设置e.Value = false,这与dbNull.Value不同,后者将再次触发dtpParse。尝试从dtpFormat

中删除e.Value = false