About strongtyped datasets, DBNulls, WPF and binding

The mix of strongtyped datasets, DBNull values, WPF and bindings will likely yield exceptions of invalid cast type when binding nullable columns.

Imagine you have a table with a single, nullable, column Description. You would bind it against a TextBox (or any other control) with this syntax: {Binding Description}. When binding is performed against a row that has a DBNull value (remember, ADO.NET uses DBNull.Value for null values) in that column you get an Unable to cast object of type ‘System.DBNull’ to type ‘System.String’ exception.

The reason for that exception is quite simple. Let’s take a look at how ADO.NET implements the strongtyped Description column:

   1: [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]

   2: [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]

   3: public bool IsDescriptionNull() {

   4:     return this.IsNull(this.tableXXX.DescriptionColumn);

   5: }

   6:  

   7: [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]

   8: [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]

   9: public string Description {

  10:     get {

  11:         try {

  12:             return ((string)(this[this.tableXXX.DescriptionColumn]));

  13:         }

  14:         catch (global::System.InvalidCastException e) {

  15:             throw new global::System.Data.StrongTypingException("The value for column \'Description\' in table \'XXX\' is DBNull.", e);

  16:         }

  17:     }

  18:     set {

  19:         this[this.tableXXX.DescriptionColumn] = value;

  20:     }

  21: }

The obvious problem is that WPF binding goes straight to Description property without checking IsDescriptionNull() method (as your code should) and then property getter fails to convert DBNull.Value to a string. And no, at this point even a converter won’t help you because the value is read before converter is even invoked.

But fear not, the solution is a very simple one. Just add square brackets around Description in binding syntax, like this: {Binding [Description]}. Looks similar but it is different. The former syntax uses strongtyped property to access the data while the later uses the DataRow’s this[string] accessor instead which returns an object and thus no exceptions are raised due to the DBNull.Value to string conversion. Furthermore a DBNullConverter value converter can come handy as well:

   1: public class DBNullConverter: IValueConverter

   2: {

   3:     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

   4:     {

   5:         if (value == DBNull.Value)

   6:             return DependencyProperty.UnsetValue;

   7:         else

   8:             // return base.Convert(value, targetType, parameter, culture);

   9:             return value;

  10:     }

  11:  

  12:     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

  13:     {

  14:         if (value == null)

  15:             return DBNull.Value;

  16:         else

  17:             // return base.ConvertBack(value, targetType, parameter, culture);

  18:             return value;

  19:     }

  20: }

Leave a Reply