7
votes

I have a select 'All' checkbox that I would like to be bound to the (IsChecked || !IsEnabled) of other checkboxes.

  • Checking 'All' checks all enabled checkboxes.

  • Unchecking 'All' unchecks all checkboxes.

  • Manually checking each enabled checkbox will check 'All'.

  • When all enabled checkboxes and 'All' are checked and user unchecks any checkbox, 'All' automatically unchecks.

I am new to WPF Databinding. I was thinking maybe I just handle the click of 'All' to set the other checkboxes. But I don't know how to bind to both properties of multiple sources. When I click 'All' and manually uncheck another checkbox, 'All' needs to uncheck.

2

2 Answers

13
votes

Here's a way to do it in Xaml with Converters. This is assuming that all your CheckBox's are added directly as Controls in Xaml (it's not very dynamic, won't work for DataTemplate's etc). First we create three CheckBoxes (CheckBox1, CheckBox2, CheckBox3) that will be Checked/Unchecked when Checking the CheckAllCheckBox. It will also work in reverse.

Update
The last part (ignore disabled CheckBox) was a bit of a problem here and I'm not crazy about this solution but I can't see a better way. We store the values from Convert and re-use them in ConvertBack for the disabled CheckBox's. Doing this, we should also add the x:Shared="False" attribute for the CheckAllConverter since a new instance is required for every MultiBinding that would use it (unlikely in this case but still..)

<Window.Resources>
    <local:CheckAllConverter x:Key="CheckAllConverter" x:Shared="False"/>
</Window.Resources>
<StackPanel>
    <CheckBox Content="Check All"
              Name="CheckAllCheckBox">
        <CheckBox.IsChecked>
            <MultiBinding Converter="{StaticResource CheckAllConverter}">
                <Binding ElementName="CheckBox1" Path="IsChecked" />
                <Binding ElementName="CheckBox1" Path="IsEnabled" Mode="OneWay"/>
                <Binding ElementName="CheckBox2" Path="IsChecked" />
                <Binding ElementName="CheckBox2" Path="IsEnabled" Mode="OneWay"/>
                <Binding ElementName="CheckBox3" Path="IsChecked" />
                <Binding ElementName="CheckBox3" Path="IsEnabled" Mode="OneWay"/>
            </MultiBinding>
        </CheckBox.IsChecked>
    </CheckBox>
    <CheckBox Content="CheckBox 1"
              Name="CheckBox1"/>
    <CheckBox Content="CheckBox 2"
              Name="CheckBox2"/>
    <CheckBox Content="CheckBox 3"
              Name="CheckBox3"/>
</StackPanel>

The Converter for CheckAll

public class CheckAllConverter : IMultiValueConverter
{
    private object[] convertValues = null;
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        convertValues = new object[values.Length];
        for(int i = 0; i < values.Length; i++)
        {
            convertValues[i] = values[i];
        }

        for (int i = 0; i < values.Length; i += 2)
        {
            bool isChecked = (bool)values[i];
            bool isEnabled = (bool)values[i + 1];
            if (isEnabled == false)
            {
                continue;
            }
            if (isChecked == false)
            {
                return false;
            }
        }
        return true;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        object[] values = new object[targetTypes.Length];
        for (int i = 0; i < values.Length; i += 2)
        {
            if (convertValues != null && (bool)convertValues[i + 1] == false)
            {
                values[i] = convertValues[i];
            }
            else
            {
                values[i] = value;
            }
            // IsEnabled is OneWay and won't care about this value
            values[i + 1] = null;
        }
        return values;
    }
}
1
votes

I'd create a ViewModel behind your View class using the MVVM design pattern: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

Then on your ViewModel (which will have to implement INotifyPropertyChanged) you can have multiple bool properties, one for each CheckBox and another for all of them:

public bool IsChecked1
{
    get
    {
         return isChecked1;
    }
    set
    {
         if (isChecked1 != value)
         {
             isChecked1 = value;
             RaisePropertyChanged("IsChecked1");
             RaisePropertyChanged("AreAllChecked");
         }
    }
}

// And so on

public bool AreAllChecked
{
    get
    {
        return IsChecked1 && IsChecked2; // etc.
    }   
    set
    {
        if (AreAllChecked != value)
        {
            IsChecked1 = value;
            IsChecked2 = value;
            // etc.
        }
    }
}