"Paul" <a@b.c> wrote in message
news:%23fe1PIHlGHA.1456@TK2MSFTNGP04.phx.gbl...
>
> "Chris Jobson" <chris.jobson@btinternet.com> wrote in message
> news:e2$61tlkGHA.4660@TK2MSFTNGP05.phx.gbl...
>> "Paul" <a@b.c> wrote in message
>> news:OL7mN3YkGHA.1000@TK2MSFTNGP04.phx.gbl...
>>> Hello,
>>>
>>> After some extensive tests, I believe I know why nullable types won't
>>> bind correctly. It comes down to the way the Parse event needs to
>>> return the value in the cevent.Value object. For example, if I have a
>>> class I want to bind to:
>>>
>>> public class Class1
>>> {
>>> private int? m_nullableInt;
>>> public int? NullableInt
>>> {
>>> get { return m_nullableInt; }
>>> set { m_nullableInt = value; }
>>> }
>>> }
>>>
>>>
>>> and I bind it to a text box
>>>
>>> Class1 a_class1 = new Class1();
>>> Binding a_binding = new Binding("Text", a_class1, "NullableInt");
>>> a_binding.Parse += new ConvertEventHandler(MyParse);
>>> MyTextBox.DataBindings.Add(a_binding);
>>>
>>> public void Parse(object sender, ConvertEventArgs cevent)
>>> {
>>> int? a_result = null;
>>>
>>> if (cevent.Value is string &&
>>> cevent.DesiredType.Equals(typeof(int?)))
>>> try { a_result = Int32.Parse(cevent.Value); } catch {}
>>>
>>> cevent.Value = a_result; // the int? gets cast down to an object,
>>> either null if a_result is null, or a boxed int if it has a value,
>>> losing its "int?" type.
>>> }
>>>
>>> Unfortunately, cevent.Value is of type object. If you assign an
>>> nullable type to an object, it boxes it for you as the underlying type.
>>> When the Parse returns to the .Net library, it is unable to call your
>>> set property because it is of the wrong type (it is an int, not a int?,
>>> so it can't successfully call the SetValue on the property).
>>>
>>> If anyone has any ideas for workarounds, or if I am completely talking
>>> rubbish, please let me know. I presume I am going to have to call the
>>> SetValue in the Parse myself?
>>
>> I'm a bit puzzled as to what the problem is. Your code works fine for me
>> (after changing "a_result = Int32.Parse(cevent.Value);" to "a_result =
>> Int32.Parse(cevent.Value.ToString());" to make it compile) and the object
>> gets updated correctly with both integer values and null. The only issue
>> I can see is that if you enter a non-integer in the text box then the
>> object's property is set to null, and the data binding then erases the
>> string in the text box - which may not be what you want.
>>
>> Could you post more details as to what isn't working.
>>
>> Chris Jobson
>>
>
> Chris, thank you for looking at this with me. I am definately not seeing
> the same thing as you. Whether or not I use my own Parse (I only added it
> to be able to set a breakpoint to see what was happening), when I tab out
> of the textbox after typing in a number, it reverts to a blank textbox.
> If I set a breakpoint on Class1.set_NullableInt, it never breaks. If I
> take the ?s out of Class1's definitions, all works as advertised.
>
> Here is the whole form code, you just need to add a textbox named textbox1
> (yes, I had originally left out the string cast, as you noted. Also the
> name of the Parse routine didn't match the one in the Event Handler set):
>
>
> using System;
> using System.Collections.Generic;
> using System.ComponentModel;
> using System.Data;
> using System.Drawing;
> using System.Text;
> using System.Windows.Forms;
> namespace FirstApp
> {
> public partial class Form1 : Form
> {
> public Form1()
> {
> InitializeComponent();
> }
> private void Form1_Load(object sender, EventArgs e)
> {
> Class1 a_class1 = new Class1();
> Binding a_binding = new Binding("Text", a_class1, "NullableInt");
> a_binding.Parse += new ConvertEventHandler(MyParse);
> textBox1.DataBindings.Add(a_binding);
> }
> public void MyParse(object sender, ConvertEventArgs cevent)
> {
> int? a_result = null;
> if (cevent.Value is string && cevent.DesiredType.Equals(typeof(int?)))
> try { a_result = Int32.Parse((string)cevent.Value); } catch { }
> cevent.Value = a_result;
> }
> }
> public class Class1
> {
> private int? m_nullableInt;
> public int? NullableInt
> {
> get { return m_nullableInt; }
> set { m_nullableInt = value; }
> }
> }
> }
>
>
> Finally, as an example of what is going on under the hood, I wrote a
> couple of lines of code to demonstrate the problem. There are 2 things
> going on: 1) The idea that a nullable type will lose its nullable-ness
> when boxed is documented on MSDN:
>
>
http://msdn2.microsoft.com/en-us/ms228597.aspx >
> 2) When you return the ConvertEventArgs.Value, if the types don't match
> EXACTLY, then the binding code will not call the Set accessor.
>
> int? a_nullableInt = 4;
> // special case boxes the underlying, not the nullable.
> object a_object = a_nullableInt;
>
> // there is no set accessor which matches type "int", so a_propertyInfo
> will be null
> PropertyInfo a_propertyInfo =
> a_class1.GetType().GetProperty("NullableInt", a_object.GetType());
> if (a_propertyInfo != null)
> a_propertyInfo.SetValue(a_class1, a_object, null);
>
> If you take the a_object.GetType() out of the call to GetProperty, and
> just get it by name, the code will get a property info back, and the call
> to SetValue actually works. But I am willing to guess that MS made sure
> that the type was specified in GetProperty() in the binding code to
> protect against an exception right off the bat (which is the case when
> types do not match -- try passing a UInt32 to SetValue() on an Int32
> property --, but GetType() on a nullable returns the underlying type,
> since GetType() forces the nullable into a box, so they can't throw an
> exception, and they let it through).
>
> Please let me know if you are still not seeing the same thing.
>
> Thanks for your time,
>
> -Paul
>
>
solution to this. In essence, it is what I proposed, that is, to call