How to avoid Converters clashing with multibinding in WPF code behind

Saggio picture Saggio · Aug 21, 2012 · Viewed 11k times · Source

I am creating WPF elements dynamically in code behind, and for each of the rows in the Grid I'm building it consists of a CheckBox and a Dynamic number of TextBoxes. The interaction that is needed is the following:

  • If all TextBoxes in a row have a value of 0, set the CheckBox IsChecked property to true and Disable it.
  • If one of the TextBoxes is then changed from 0, enable the CheckBox and set IsChecked to false.
  • If the user clicks on the CheckBox, set all associated TextBoxes to 0 and Disable the CheckBox

I was able to accomplish the first part of the last one using this code:

Binding setScoreToZeroIfIsNormalChecked = new Binding("IsChecked");
setScoreToZeroIfIsNormalChecked.Source = this.NormalCheckBoxControl;
setScoreToZeroIfIsNormalChecked.Converter = m_NormalCheckBoxJointScoresConverter;
tempJointScoreControl.JointScoreContainer.SetBinding(ContainerBase.SingleAnswerProperty, setScoreToZeroIfIsNormalChecked);

and the converter:

public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    if (value is bool && targetType == typeof(Answer))
    {
        if ((bool)value)
        {
            Answer answer = new Answer();
            answer.Value = "0";
            answer.DisplayValue = "0";
            return answer;
        }
        else
            return null;
    }
    else
    {
        return null;
    }
}

However, in attempting to create another converter to accomplish other functionality, I was running into issues of converters stepping on one another since all functionality is based around the CheckBox.IsChecked property.

Is there anyway to accomplish all of the above using one or two multibinding converters? I'd really like to avoid having to create a whole bunch of events and maintaining them in order to do this.

Answer

Erti-Chris Eelmaa picture Erti-Chris Eelmaa · Aug 21, 2012

It's relatively easy. Everything should resolve around CheckBox IsChecked property. For a simple reason, it's a two-way property. So either you can modify it, or CheckBox can modify it.

So what you do, you use MultiBinding, as such:

    MultiBinding multiBinding = new MultiBinding();
    multiBinding.Converter = multiBindingConverter;

    multiBinding.Bindings.Add(new Binding("Text") { Source = txtbox1});
    multiBinding.Bindings.Add(new Binding("Text") { Source = txtbox2});

    multiBinding.NotifyOnSourceUpdated = true;//this is important. 
    checkBox.SetBinding(CheckBox.IsCheckedProperty, multiBinding);

And in your multiBindingConverter, you will have object[] value as first parameter, which you need to convert into IList and iterate over it && do your calculations, if you should either return true/false.(IsChecked=true or false)

Now bind CheckBox IsEnabled to CheckBox IsChecked property, and use BooleanInverterConverter. (If CheckBox is checked, it should be disabled, and vice versa)

The last step is to make TextBoxes listen to actual IsChecked property of CheckBox. If it is TRUE, they all should show value of 0, otherwise they can show what they want.

So, make a new MultiBinding.

    MultiBinding multiBinding = new MultiBinding();
    multiBinding.Converter = textboxMultiBindingConverter;

    multiBinding.Bindings.Add(new Binding("IsChecked") { Source = checkbox1});
    multiBinding.Bindings.Add(new Binding("Text") { Source = textbox1});

    multiBinding.NotifyOnSourceUpdated = true;//this is important. 
    textbox1.SetBinding(TextBox.Text, multiBinding);

the idea in textboxMultiBindingConverter is to either return Text(value[1]) if value[0]==FALSE or "0" if value[0]==TRUE.