8
votes

How do I use an IValueConverter to convert nulls into booleans?

I'm using wpf to try to display a bunch of boolean values (in checkboxes). When a new record is created, these values are null, and appear as 'indeterminate' in the checkboxes. I want the nulls to appear and save as 'false' values.

I tried to create a NullToBoolean converter that takes null values from the database and displays them as false, and then saves them as false when the user hits save. (Essentially, I'm trying to avoid the user having to click twice in the checkboxes (once to make it true, then again to make it false). This seems to work on import - ie null values are shown as false - but unless I do the two-click dance the value doesn't change in the database when I save.

My Converter:

[ValueConversion(typeof(bool), typeof(bool))]
public class NullBooleanConverter : IValueConverter
{

  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (value != null)
    {
      return value;
    }
    return false;
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (value != null)
    {
      return value;
    }
    return null;
  }
}

One of the checkboxes I'm trying to have the Converter work with:

    <CheckBox Grid.Column="1" Grid.Row="0" Padding="5" Margin="5" VerticalAlignment="Center" Name="chkVarianceDescriptionProvided" IsThreeState="False">
      <CheckBox.IsChecked>
        <Binding Path="VarianceDescriptionProvided" Mode="TwoWay">
          <Binding.Converter>
            <utils:NullBooleanConverter />
          </Binding.Converter>
        </Binding>
      </CheckBox.IsChecked>
    </CheckBox>

I don't know if the problem is because my code is wrong, or if it's a case of the Converter thinking that nothing has changed, therefore it doesn't need to ConvertBack. I have tried all the Modes and switched code in Convert with ConvertBack, but nothing seems to work.

Can someone point out what I need to do to fix this?

3
Man that looks ugly. Are you converting Null to false and not-null to true? Because you are not doing that. and on convert back, you're converting bool (which will never be null) back to a bool.Meirion Hughes
Did you check if your converter code is hitting?Sandeep Singh Rawat
By the way, "When a new record is created, these values are null..." is the problem; fix that. You should be making "view friendly" ViewModels / DataModels.Meirion Hughes
@MeirionHughes I think your comment would be a better way to solve this than what I had put in my answer. I think you can put that as an answer yourself.user743382
@MeirionHughes. No. Null to false and everything else (always boolean) to the given value. Any beautifying advice gladly taken on board :-). Comment 2) They are nullable in the database (which i can't change) and EF is taking care of all the creation. I thought I could use the ValueConverter to create a view-friendly ViewModel. To Sandeep Singh Rawat: yep, I've taken out my MessageBox/debug code, but am definitely hitting the methods.mcalex

3 Answers

21
votes

Hmm, why using a converter, if you can have it out of the box?

<CheckBox IsChecked="{Binding VarianceDescriptionProvided, TargetNullValue=False}" />

For more information, pls have a look here.

1
votes

The real problem is the fact you are not initializing your data objects in the first place. Don't "fix", do it right to begin with; builders are good (for example). You also should be making ViewModels/DataModels rather than working with your Models (database, etc) directly.

public class MyObjectBuilder
{
     Checked _checked;

     public  MyObjectBuilder()
     {
          Reset()
     }

     private void Reset()
     { 
          _checked = new Checked(true); //etc
     }

     public MyObjectBuilder WithChecked(bool checked)
     {
          _checked = new Checked(checked);
     }

     public MyObject Build()
     {
         var built = new MyObject(){Checked = _checked;} 
         Reset();
         return built;
     }
}

then always initialise with the builder

myObjects.Add(new MyObjectBuilder().Build());

or

myObjects.Add(_injectedBuilder.Build()); // Initialises Checked to default 
myObjects.Add(_injectedBuilder.WithChecked(true).Build()); //True

While this doesn't fix your asked problem, it will fix your underlying problem in a way you can Unit Test. i.e. you can test to ensure the values added into your object list are always initialized.

0
votes

Simply correct your data before you perform data binding. That is the only option. The converter will only work make the check box show as 'unchecked' and update your data only when you interact with the control. For example:

foreach (var item in items)
{
    if (item.VarianceDescriptionProvided == null)
        item.VarianceDescriptionProvided = false;
}