Posts Tagged ‘DataGrid’

The DataGrid and the “Input string is not in a correct format” message in Silverlight

Sunday, December 20th, 2009

If you play around with the DataGrid in Silverlight and try some scenarios, maybe you come around the FormatException with the Message “Input String is not in a correct format”. You get this Exception if your Data-Object e.g. has a Property of type int and the user enters some characters in the DataGrid. The Exception doesn’t come up, instead the DataGrid shows it as a validation error. Let’s look at an example. Image you’ve a very simple Person-class containing a FirstName-Property of type string and an Age-Property of type int:

public class Person : INotifyPropertyChanged
{
  private int? _age;
  private string _firstName;
  public string FirstName
  {
    get { return _firstName; }
    set
    {
      _firstName = value;
      Changed("FirstName");
    }
  }

  public int? Age
  {
    get { return _age; }
    set
    {
      _age = value;
      Changed("Age");
    }
  }

  private void Changed(string propertyName)
  {
    if (PropertyChanged != null)
      PropertyChanged(this, 
        new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

Now let’s fill up a List with some Test-Persons and add that list to the DataContext of your page:

public MainPage()
{
  InitializeComponent();
  this.DataContext = new List<Person> { 
    new Person{ FirstName="Thomas",Age=29},
    new Person{ FirstName="Julia",Age=27},
    new Person{ FirstName="Ben",Age=1},
  };
}

No in the XAML-File of your MainPage a DataGrid could be defined like below. Notice the ItemsSource-Property that is bound to the DataContext:

<my:DataGrid ItemsSource="{Binding}"
             AutoGenerateColumns="False">     
  <my:DataGrid.Columns>
    <my:DataGridTextColumn Header="FirstName"
                           Binding="{Binding FirstName}"/>
    <my:DataGridTextColumn Header="Age"
                           Binding="{Binding Age}"/>
  </my:DataGrid.Columns>
</my:DataGrid>

When the User now enters a string as Age, the FormatException is raised before the Property is updated and the Message of the FormatException is displayed in the DataGrid:

image

Now the question is, where to change this string. The DataGrid has a BindingValidationError-Event, but there you have only read access to the FormatException and the Errormessage. In the Property itself you can’t do anything, because the Exception is thrown before the Age-Property is set.

The solution is to define a Property special for Display in the Person-class. Normally you would create a ViewModel that encapsulates the Person-class and contains the additional property. In my case I implement the Property directly in the Person-class. I call it AgeDisplay-Property. Inside that property you can then throw your own FormatException with your special text. My Person-class now looks like this:

public class Person : INotifyPropertyChanged
{
  private int? _age;
  private string _firstName;
  public string FirstName
  {
    get { return _firstName; }
    set
    {
      _firstName = value;
      Changed("FirstName");
    }
  }

  public int? Age
  {
    get { return _age; }
    set
    {
      _age = value;
      Changed("Age");
      Changed("AgeDisplay");
    }
  }


  public string AgeDisplay
  {
    get { return Age.ToString(); ; }
    set
    {
      if (value == null)
      {
        Age = null;
        return;
      }
      int result;
      if (int.TryParse(value, out result))
      {
        Age = result;
      }
      else
      {
        throw new FormatException("Age must be a number. "
                        + " Characters are not allowed.");
      }
    }
  }

  private void Changed(string propertyName)
  {
    if (PropertyChanged != null)
      PropertyChanged(this, 
        new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

No you simple bind the DataGrid’s Column to AgeDisplay instead of Age:

<my:DataGrid ItemsSource="{Binding}"
             AutoGenerateColumns="False">     
  <my:DataGrid.Columns>
    <my:DataGridTextColumn Header="FirstName"
                           Binding="{Binding FirstName}"/>
    <my:DataGridTextColumn Header="Age"
                           Binding="{Binding AgeDisplay}"/>
  </my:DataGrid.Columns>
</my:DataGrid>

When the user now enters a string into the Age-Column, the text of the FormatException thrown in the AgeDisplay-Property is displayed:

image