Binding to Nullable(Of T) properties

J

Jack Jackson

In VS2005 VB, is it possible to bind a Nullable property to a data
source (e.g. DataTable) property that is not Nullable? My goal is to
be able to convert DBNull to and from Nothing for Value data types.

I created a derived Binding object with Format and Parse handlers. In
the Format event I am unable to set the event arg's Value property to
a Nullable type. For example, stripping out the code that is not
germane to this discussion:

Protected Overrides Sub OnFormat(ByVal cevent As
System.Windows.Forms.ConvertEventArgs)
dim dt As Nullable(Of DateTime)

dt = Convert.ToDateTime(cevent.Value)
cevent.Value = dt

cevent.Value is defined as Object. The last line sets cevent.Value to
a DateTime, not to a Nullable(Of DateTime). In playing around with
this, I have been unable to get a property defined as Object set to a
Nullable - it always sets it to the value property of the Nullable
object.

Because cevent.Value is a DateTime I get an invalid cast error when
..NET tries to set the Nullable property to cevent.Value.

Is there any way to do the binding?
 
M

Morten Wennevik [C# MVP]

Hi Jack,

Using Format and Parse events is the way to do it. Use the
ConvertEventArgs.DesiredType property to see what kind of type cevent.Value
should be before you leave the event method. ConvertEventArgs.Value will
return an Object, but this object will be a DateTime, a nullable DateTime
null or DBNull.Value

Your code does not appear to take into account that cevent.Value may in fact
be null, in which case cevent.Value should be set to DBNull.Value.

DateTime? nullableDate = cevent.Value as DateTime?;
cevent.Value = nullableDate.Value ?? DBNull.Value;

The last line is equal to
if(nullableDate == null)
cevent.Value = DBNull.Value;
else
cevent.Value = nullableDate.Value;
 
J

Jack Jackson

Hi Jack,

Using Format and Parse events is the way to do it. Use the
ConvertEventArgs.DesiredType property to see what kind of type cevent.Value
should be before you leave the event method. ConvertEventArgs.Value will
return an Object, but this object will be a DateTime, a nullable DateTime
null or DBNull.Value

In the Format event I need to return a nullable DateTime since that is
the type of the bound property, but I am unable to do that. As I said
in the original post,
Dim dt as Nullable(Of DateTime)
dt = <some DateTime value>
cevent.Value = dt
makes cevent.Value be a DateTime, not a nullable DateTime. Returning
a DateTime results in an invalid cast exception (DateTime can not be
cast to Nullable).
Your code does not appear to take into account that cevent.Value may in fact
be null, in which case cevent.Value should be set to DBNull.Value.

That is correct. I removed that code since it was not germane to my
problem.
 
M

Morten Wennevik [C# MVP]

Hi Jack,

I think I may be misunderstanding your problem. The Format event is for
converting datasource properties to control properties. You are saying you
need to return a nullable DateTime in the Format event, but setting
cevent.Value to a nullable DateTime makes no sense unless the Control
property supports null.

The Format event would be responsible to convert a nullable datetime to
either a DateTime or DBNull.Value, whereas the Parse event would be
responsible to convert either a DateTime or DBNull.Value to a nullable
DateTime.

If you could show is some more code it may be easier to spot the error. How
do you set up your databinding?

Try using separate references for the DateTime vs nullable DateTime instead
of reusing. dt = Convert.ToDateTime(cevent.Value) would not compile in C#
because Convert.ToDateTime returns a DateTime reference whereas dt is a
nullable DateTime. VB does some type conversion behind the scenes that not
always works perfectly.
 
J

Jack Jackson

Thanks for your interest in my problem.

I have an object property that is a Nullable(Of DateTime) that I am
attempting to bind to a data source property that is a DateTime.

I have a custom Binding object to connect the two. If I don't
override OnFormat, I get an InvalidCastException when the binding
tries to cast the data source DateTime to the Nullable(Of DateTime) of
the object.

To fix this I override Binding.OnFormat. ConvertEventArgs.Value is a
DateTime, which I need to turn into a Nullable(Of DateTime) to avoid
the illegal cast exception.

My problem is that I am unable to get ConvertEventArgs.Value to be a
Nullable(Of DateTime). This code (cevent is the ConvertEventArgs
argument):
Dim dt As Nullable(Of DateTime)
dt = Convert.ToDateTime(cevent.Value)
cevent.Value = dt
results in cevent.Value being a DateTime, not a Nullable(Of DateTime).

The only way I have found to get cevent.Value to be a Nullable(Of
DateTime) is to use Activator.CreateInstance, but that creates a
Nullable with HasValue = False, and I can't find anyway to give it a
value.

Hi Jack,

I think I may be misunderstanding your problem. The Format event is for
converting datasource properties to control properties. You are saying you
need to return a nullable DateTime in the Format event, but setting
cevent.Value to a nullable DateTime makes no sense unless the Control
property supports null.

The control property is a Nullable(Of DateTime)
The Format event would be responsible to convert a nullable datetime to
either a DateTime or DBNull.Value, whereas the Parse event would be
responsible to convert either a DateTime or DBNull.Value to a nullable
DateTime.

I believe you have reversed Format and Parse. Format converts the
binding source property (DateTime) to the control property
(Nullable(Of DateTime)).
If you could show is some more code it may be easier to spot the error. How
do you set up your databinding?

Try using separate references for the DateTime vs nullable DateTime instead
of reusing. dt = Convert.ToDateTime(cevent.Value) would not compile in C#
because Convert.ToDateTime returns a DateTime reference whereas dt is a
nullable DateTime.

I am not reusing dt. In VB you set the value of a Nullable by
assigning an item of the Of T type to it:
Dim dt As Nullable(Of DateTime)
dt = Date.Now()
The Convert is necessary in this case because cevent.Value is defined
as Object and must be cast to DateTime. The result of this code is
that dt is a Nullable(Of DateTime) with a value of Date.Now().

I am aware that VS2005 VB has less support for Nullable than C#, and
it may be that I simply can't do this in VS2005 VB.
 
M

Morten Wennevik [C# MVP]

Hi Jack,

I'll take a closer look at it tomorrow. In the meantime try changing your
event code to something like


Dim dt as DateTime
dt = Convert.ToDateTime(cevent.Value)
cevent.Value = dt

Or

Dim dt = cevent.Value as Nullable(Of DateTime) ' may not be syntactically
right
cevent.Value = dt.Value


Your code as it was would be illegal in C# due to type difference.
Furthermore for datetime and nullable datetime the types are considered equal
even though in the end they are not.
--
Happy Coding!
Morten Wennevik [C# MVP]


Morten Wennevik said:
Hi Jack,

I think I may be misunderstanding your problem. The Format event is for
converting datasource properties to control properties. You are saying you
need to return a nullable DateTime in the Format event, but setting
cevent.Value to a nullable DateTime makes no sense unless the Control
property supports null.

The Format event would be responsible to convert a nullable datetime to
either a DateTime or DBNull.Value, whereas the Parse event would be
responsible to convert either a DateTime or DBNull.Value to a nullable
DateTime.

If you could show is some more code it may be easier to spot the error. How
do you set up your databinding?

Try using separate references for the DateTime vs nullable DateTime instead
of reusing. dt = Convert.ToDateTime(cevent.Value) would not compile in C#
because Convert.ToDateTime returns a DateTime reference whereas dt is a
nullable DateTime. VB does some type conversion behind the scenes that not
always works perfectly.

--
Happy Coding!
Morten Wennevik [C# MVP]


Jack Jackson said:
In the Format event I need to return a nullable DateTime since that is
the type of the bound property, but I am unable to do that. As I said
in the original post,
Dim dt as Nullable(Of DateTime)
dt = <some DateTime value>
cevent.Value = dt
makes cevent.Value be a DateTime, not a nullable DateTime. Returning
a DateTime results in an invalid cast exception (DateTime can not be
cast to Nullable).


That is correct. I removed that code since it was not germane to my
problem.
 
J

Jack Jackson

I found a way to avoid these problems with Nullable by setting
Binding.FormattingEnabled = True. Apparently this lets the Binding
object convert between DateTime and Nullable(Of DateTime).

I have decided it is impossible to set an Object to a Nullable. See
below.

Hi Jack,

I'll take a closer look at it tomorrow. In the meantime try changing your
event code to something like


Dim dt as DateTime
dt = Convert.ToDateTime(cevent.Value)
cevent.Value = dt

Since cevent.Value already is a DateTime, this will have no effect and
results in an InvalidCastException when the Binding tries to convert
cevent.Value (DateTime) to the type of the bound object (Nullable(Of
DateTime)).
Or

Dim dt = cevent.Value as Nullable(Of DateTime) ' may not be syntactically
right
cevent.Value = dt.Value

I think this is essentially what I first tried:
Dim dt As Nullable(Of DateTime) = Convert.ToDateTime(cevent.Value)
cevent.Value = dt
which results in cevent.Value being a DateTime, not a Nullable(Of
DateTime).
Your code as it was would be illegal in C# due to type difference.

I don't think so. My understanding of the C# syntax is:
int? x = 10;
which is just what I am doing in VB. I assign a DateTime value to my
Nullable(Of DateTime) variable.
Furthermore for datetime and nullable datetime the types are considered equal
even though in the end they are not.

This can't be entirely correct given the InvalidCastException that I
get.

I am beginning to think there simply is no way to assign a Nullable
item to an item defined as Object and have the result be a Nullable.
In fact, I'm pretty sure this is true. At
<http://www.theserverside.net/news/thread.tss?thread_id=36085> I found
this:

"The outcome is that the Nullable type is now a new basic runtime
intrinsic. It is still declared as a generic value-type, yet the
runtime treats it special. One of the foremost changes is that boxing
now honors the null state. A Nullabe int now boxes to become not a
boxed Nullable int but a boxed int (or a null reference as the null
state may indicate.) Likewise, it is now possible to unbox any kind of
boxed value-type into its Nullable type equivalent."

When the Nullable is assigned to an Object, it must be boxed, and the
above says that it will be boxed as its internal type not a Nullable.

So maybe this is a 'bug' in Binding that should deal with this
situation but doesn't.
 
M

Morten Wennevik [C# MVP]

Hi Jack,

Jack Jackson said:
I found a way to avoid these problems with Nullable by setting
Binding.FormattingEnabled = True. Apparently this lets the Binding
object convert between DateTime and Nullable(Of DateTime).

Ah, that might help. Also, sometimes calling bindingsource.ResetBindings
will help initial updates although that may not be related to this case. I'm
glad it solved your problem.
Since cevent.Value already is a DateTime, this will have no effect and
results in an InvalidCastException when the Binding tries to convert
cevent.Value (DateTime) to the type of the bound object (Nullable(Of
DateTime)).

Hm, I was under impression that cevent.Value was passed as nullable DateTime
I think this is essentially what I first tried:
Dim dt As Nullable(Of DateTime) = Convert.ToDateTime(cevent.Value)
cevent.Value = dt
which results in cevent.Value being a DateTime, not a Nullable(Of
DateTime).


I don't think so. My understanding of the C# syntax is:
int? x = 10;
which is just what I am doing in VB. I assign a DateTime value to my
Nullable(Of DateTime) variable.

I may have been wrong on this one and am unable to find the code that
produced the compiler error or reproduce the error. However, both in case
int? x = 10 and DateTime? y = DateTime.Today you will end up with a nullable
type.
This can't be entirely correct given the InvalidCastException that I
get.

DateTime? x = new DateTime?(DateTime.Today)
DateTime y = DateTime.Now;

x.GetType() == y.GetType()

It appears that this type equality is not entirely true and while the
compiler accepts it, the runtime might not.
I am beginning to think there simply is no way to assign a Nullable
item to an item defined as Object and have the result be a Nullable.
In fact, I'm pretty sure this is true. At
<http://www.theserverside.net/news/thread.tss?thread_id=36085> I found
this:

"The outcome is that the Nullable type is now a new basic runtime
intrinsic. It is still declared as a generic value-type, yet the
runtime treats it special. One of the foremost changes is that boxing
now honors the null state. A Nullabe int now boxes to become not a
boxed Nullable int but a boxed int (or a null reference as the null
state may indicate.) Likewise, it is now possible to unbox any kind of
boxed value-type into its Nullable type equivalent."

When the Nullable is assigned to an Object, it must be boxed, and the
above says that it will be boxed as its internal type not a Nullable.

It is boxed as its internal datatype when it isn't null, if it IS null it
will be stored as a null reference.
So maybe this is a 'bug' in Binding that should deal with this
situation but doesn't.

I really wish you provided more detail information about your setup than
vague ideas. That way we can try to reproduce the issue locally and maybe
find a solution or workaround.
 
J

Jack Jackson

Thanks for your help.

Hi Jack,



Ah, that might help. Also, sometimes calling bindingsource.ResetBindings
will help initial updates although that may not be related to this case. I'm
glad it solved your problem.


Hm, I was under impression that cevent.Value was passed as nullable DateTime


I may have been wrong on this one and am unable to find the code that
produced the compiler error or reproduce the error. However, both in case
int? x = 10 and DateTime? y = DateTime.Today you will end up with a nullable
type.


DateTime? x = new DateTime?(DateTime.Today)
DateTime y = DateTime.Now;

x.GetType() == y.GetType()

It appears that this type equality is not entirely true and while the
compiler accepts it, the runtime might not.


It is boxed as its internal datatype when it isn't null, if it IS null it
will be stored as a null reference.


I really wish you provided more detail information about your setup than
vague ideas. That way we can try to reproduce the issue locally and maybe
find a solution or workaround.
 
M

Mat

Hi Guys,

I had the same problem but found this reference on the internet. Works like
a charm and bypasses the need to do backflips.

http://blog.jezhumble.net/?p=3

--Binding to nullables work in WinForms.
--I had the same problem before where the nullable won’t be updated. However,
--passing extra arguments seem to fix it for some reason (haven’t checked
into the
--details yet), e.g.
--bind = new Binding(â€ValueObjectâ€, bindingSource, “FeeAmountâ€, true,
-- DataSourceUpdateMode.OnPropertyChanged);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top