WPF绑定复选框IsChecked到其他复选框(IsChecked ||!IsEnabled)

时间:2022-11-07 13:44:03

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

我有一个选择'全部'复选框,我想绑定到其他复选框的(IsChecked ||!IsEnabled)。

  • 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.

我是WPF Databinding的新手。我想也许我只是点击“全部”来设置其他复选框。但我不知道如何绑定到多个源的两个属性。当我点击“全部”并手动取消选中另一个复选框时,“全部”需要取消选中。

2 个解决方案

#1


12  

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.

这是使用转换器在Xaml中执行此操作的方法。这假设您的所有CheckBox都直接作为Xaml中的控件添加(它不是非常动态,不适用于DataTemplate等)。首先,我们创建三个CheckBoxes(CheckBox1,CheckBox2,CheckBox3),在检查CheckAllCheckBox时将检查/取消选中。它也会反向运作。

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..)

更新最后一部分(忽略禁用的CheckBox)在这里有点问题,我并不是对这个解决方案感到疯狂,但我看不到更好的方法。我们存储Convert的值,并在ConvertBack中重用它们用于禁用的CheckBox。这样做,我们还应该为CheckAllConverter添加x:Shared =“False”属性,因为每个使用它的MultiBinding都需要一个新实例(在这种情况下不太可能但仍然......)

<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

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;
    }
}

#2


1  

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

我将使用MVVM设计模式在View类后面创建一个ViewModel: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:

然后在您的ViewModel(必须实现INotifyPropertyChanged)上,您可以拥有多个bool属性,每个CheckBox一个,另一个用于所有这些属性:

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.
        }
    }
}  

#1


12  

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.

这是使用转换器在Xaml中执行此操作的方法。这假设您的所有CheckBox都直接作为Xaml中的控件添加(它不是非常动态,不适用于DataTemplate等)。首先,我们创建三个CheckBoxes(CheckBox1,CheckBox2,CheckBox3),在检查CheckAllCheckBox时将检查/取消选中。它也会反向运作。

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..)

更新最后一部分(忽略禁用的CheckBox)在这里有点问题,我并不是对这个解决方案感到疯狂,但我看不到更好的方法。我们存储Convert的值,并在ConvertBack中重用它们用于禁用的CheckBox。这样做,我们还应该为CheckAllConverter添加x:Shared =“False”属性,因为每个使用它的MultiBinding都需要一个新实例(在这种情况下不太可能但仍然......)

<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

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;
    }
}

#2


1  

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

我将使用MVVM设计模式在View类后面创建一个ViewModel: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:

然后在您的ViewModel(必须实现INotifyPropertyChanged)上,您可以拥有多个bool属性,每个CheckBox一个,另一个用于所有这些属性:

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.
        }
    }
}