Link to home
Start Free TrialLog in
Avatar of FrancineTaylor
FrancineTaylor

asked on

Binding Manager Add.New() causing hangups on bound DateTime column (null value?)


I have a simple form bound to a data table.  The code was copied from a program that works, and everything about the new form works except the "Add New" functionality.

Clicking the "Add New" button executes this code:
 
     myBindingManager.AddNew();

The behavior I see is that:

    The bound controls do not automatically reset to blank values like they should.
     A row is added (myBindingManager.Count is incremented) but changing the position of the binding manager
          to that row (myBindingManager.Position ++) doesn't work; it refuses to advance to the new row.

After fooling around a bit, I discovered that the hangup seems to be that I have added a DataTimePicker control and bound it to a DateTime type column:

myDateTimePickerControl.DataBindings.Add (new Binding("Value", dsMyDataSet, "tablename.datetimecolumnname"));

When changing the binding manager position to move through the rows everything works fine if there is a value in the column, but if it is null (as it is when a new record is added) the binding manager chokes.

1. How can I set up my binding to handle null values in the columns, and
2. Which column types besides DateTime will probably be set to null on an Add.New()?
Avatar of Alexandre Simões
Alexandre Simões
Flag of Switzerland image


Hi!

If you're using the framework 2.0 the Binding object have some new properties.
One of the is on one constructor overload and is named NullValue. This will change the value to a default whenever the source value is null.

If you're still on FW 1.0 or 1.1 then I believe this isn't as easy.

http://www.codeproject.com/cs/miscctrl/Nullable_DateTimePicker.asp
http://www.dotnet247.com/247reference/msgs/32/162933.aspx
http://blogs.duncanmackenzie.net/duncanma/archive/2003/02/28.aspx

Alex :p
Avatar of sumix
sumix


Maybe you should consider setting default values for datetime column
    dsMyDataSet.tablename.Columns["datetimecolumnname"].DefaultValue = DateTime.Now;

Every column will have a null value after AddNew method if it doesn't have a default value set. The behavior you mention is also determined by boolean columns.
Avatar of FrancineTaylor

ASKER

I like your solution, sumix, and it will solve the immediate problem for those columns which can be forced to accept a date, but in the case of Date Fired for employee I can't put a default value in, nulls have to be allowed.

I need a way to detect whether the is a null value in the column or not.  If there is a null, I can set the datetimepicker to a blank value.  But I need to be able to intercept the values going into to bound controls.  I vaguely recall that there is such a mechanism, but I haven't been able to track it down.

The default DateTimePicker doesn't support blank (null) values...
You'll have to make one that supports (as one on the links I gave you) or set a default value...

Alex :p

I would like to comment this:
"But I need to be able to intercept the values going into to bound controls."

Based on what I previously said, even if you in some sort of way, had a way to intercept the values between the datasource and the bound controls, what would you do?
     - Set them to a certain value if null? (seems like a default value behavior)
     - Leave them null? (same as nothing)
     - What else could be an option?...


Alex :p
ASKER CERTIFIED SOLUTION
Avatar of sumix
sumix

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

As I see it, the Format event isn't an option either...
He still can't show an empty DateTimePicker... it must have a value...

We can use that event when we needed to format data that come from the DB into readonly textboxes...
If the textbox is editable it won't format it on validate...
Anyway, I usually use a custom textbox control that formats the data for presentation (currency format for ex...)

Alex
Actually, showing an empty datetime isn't too difficult.  A little unwieldly, perhaps.

Here is the method I use:

static public void SetDateTimePicker(DateTimePicker pdtp, Boolean pbSetToNull)
{
      if (pbSetToNull)
      {
            pdtp.Format = DateTimePickerFormat.Custom;
            pdtp.CustomFormat = " ";
      }
      else
      {
            pdtp.Format = DateTimePickerFormat.Short;
      }
}

In my form_load, I have this code:

        SetDateTimePicker(myDateTimePicker, true);

Then, I create this event method:

      private void myDateTimePicker_ValueChanged(object sender, System.EventArgs e)
      {
            SetDateTimePicker(myDateTimePicker, false);
      }

Datepickers start out with blank values, then when the user selects a value, the artificially imposed "blankness" is removed.


Althogh it may work you'll have to code that every time you have to implement such feature.
Wouldn't it be wiser to create your custom control and just reuse it?...

Alex :p
You have a good point, but it's still a hard call to make.  Some of the considerations:

I like to design my standalone applications so that they are easily portable to web applications.  I'm hoping (tho I haven't yet had time to do the research) that Parse and Format are available for web controls.

As you mentioned, I'd have to code the Parse and Format into every application.  That would be a bit tedious.

If I went with a custom control, I'd need to include that control in every project, which means there would be multiple copies of it.  If changes were made, I'd either have to have multiple versions of the control, or I'd have to recompile and retest with every application.

And the biggest consideration: I haven't yet gotten a workable modified datetimepicker control that will compile on my computer.  I pulled down the one from Code Project, but when I unzipped it and tried to compile it told me the NullableDateTimePicker.resx file was missing.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for all the help, btw, Alex,

Okay, I grabbed this code, put it into a class, did a simple test just using the control and it looks like it works great for allowing you to clear a date control.

However, I added databinding to the form, binding the control to a date type column, and I ended up with exactly the same problem I had with a simple DateTimePicker.  The control chokes when the data in the dataset has a value of <null>.

When I tried adding code in the "set" method to check for a value of null, I got an error.  A value of "null" simply cannot be passed in to the control.  The base value property won't support it.

How do I get this control to work with databinding in the cases where the value is null, assuming that I want the value to continue to stay null when the changes are sent back to the database?
Maybe a split is in order here...

Alex :p
I decided to split the points (sorry it took me so long to resolve this) the way I did because the Format / Parse technique was necessary even with the custom datetimepicker.   The custom dtpicker was nice, though it had to be modified further before it would work.  You can't set the datetimepicker to DateTime.MinValue, because the minimum date the control will accept is 1/1/1753.

This is the codebehind for the custom control after I got done with it:

public class myDateTimePicker : System.Windows.Forms.DateTimePicker  
{
      private bool bIsNull = false;

      // this is the minimum value that a standard date control will take
      public static DateTime nullvalue = System.DateTime.Parse("01/01/1753");
      private DateTimePickerFormat format;

      public myDateTimePicker() : base()
      {
            this.format = base.Format;
      }

      public new DateTime Value
      {
            get
            {
                  if (bIsNull)
                        return nullvalue;
                  else
                        return base.Value;
            }
            set
            {
                  if (value == DateTime.MinValue || value == nullvalue)
                  {
                        if (this.bIsNull == false)
                        {
                              //oldCustomFormat = this.CustomFormat;
                              this.bIsNull = true;
                        }

                        this.Format = DateTimePickerFormat.Custom;
                        this.CustomFormat = " ";
                  }
                  else
                  {
                        if (bIsNull)
                        {
                              this.Format = this.format;
                              this.bIsNull = false;
                        }
                        base.Value = value;
                  }
            }
      }
      public void SetToNull()
      {
            if (!bIsNull)
            {
                  this.Value = nullvalue;
            }
      }
      public Boolean IsNull()
      {
            return this.bIsNull;
      }
      public void SetFormat(DateTimePickerFormat pf)
      {
            this.format = pf;
            if (!this.bIsNull)
                  this.Format = pf;
      }
      protected override void OnCloseUp(EventArgs eventargs)
      {
            if (Control.MouseButtons == MouseButtons.None)
            {
                  if (this.bIsNull)
                  {
                        this.Format = this.format;
                        this.bIsNull = false;
                  }
            }
            base.OnCloseUp (eventargs);
      }

      protected override void OnKeyDown(KeyEventArgs e)
      {
            base.OnKeyDown (e);

            if (e.KeyCode == Keys.Delete)
                  this.Value = nullvalue;
      }
}  // class myDateTimePicker



Here is how I ended up implementing it:

// here is the module level declaration of the custom control
private myDateTimePicker dtFired;

// this goes in the form load
CreateDateBinding("employee.fired", dtFired);

// and here are the methods that go with the databinding
private void FormatDateBinding(object sender, ConvertEventArgs cevent)
{
      if (cevent.DesiredType != typeof(DateTime))
      {
            cevent.Value =  myDateTimePicker.nullvalue;
      }
      else if (cevent.Value == System.DBNull.Value)
      {
            cevent.Value = myDateTimePicker.nullvalue;
      }
}
private void ParseDateBinding(object sender, ConvertEventArgs cevent)
{
      if ((DateTime) cevent.Value == myDateTimePicker.nullvalue)
            cevent.Value = System.DBNull.Value;
}
private void CreateDateBinding(String psColumn, myDateTimePicker pdt)
{
      Binding bn = new Binding("Value", dsEmployee, psColumn);
      bn.Format += new ConvertEventHandler(this.FormatDateBinding);
      bn.Parse += new ConvertEventHandler(this.ParseDateBinding);
      pdt.DataBindings.Add(bn);
}

Thanks to both sumix and AlexCode for the solution!