检查复选框列表中所有已启用项目的状态

时间:2018-01-15 20:00:53

标签: c# wpf mvvm

围绕mvvm编程模式我的头脑。绝对是一种不同的思维方式。

此时,我有一个简单的问题。虽然我当然可以“蛮力”一个解决方案,但我确信那里有一些优雅的东西,我只是想念它。

我有一个复选框列表,其中一些已启用,一些已禁用。这是怎么回事无关紧要。我还有一个Select All toggle。

enter image description here

我想要做的是,能够评估启用的功能,查看它们是否已被选中(确定是否显示“全部选择”作为选中)或选择所有启用的功能(忽略禁用的)然后在选择全部旁边显示一个检查。

就MVVM方法而言,“如何”对我来说还不清楚。我到目前为止炮制的内容:

XAML:

return &listeners;

代码:

<StackPanel Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Top">
    <Label x:Name="labelFeatures" Content="Features" FontWeight="Bold" />
    <CheckBox x:Name="cb_FEAT_All" Content="Select All" IsChecked="{Binding AllFeatures}" Margin="6,0,0,0" />
    <CheckBox x:Name="cb_FEAT_1" Content="Feature 1" Margin="20,0,0,0" IsEnabled="{Binding Feature1Enabled}" IsChecked="{Binding Using_Feat_1}"/>
    <CheckBox x:Name="cb_FEAT_2" Content="Feature 2" Margin="20,0,0,0" IsEnabled="{Binding Feature2Enabled}" IsChecked="{Binding Using_Feat_2}"/>
    <CheckBox x:Name="cb_FEAT_3" Content="Feature 3" Margin="20,0,0,0" IsEnabled="{Binding Feature3Enabled}" IsChecked="{Binding Using_Feat_3}"/>
    <CheckBox x:Name="cb_FEAT_4" Content="Feature 4" Margin="20,0,0,0" IsEnabled="{Binding Feature4Enabled}" IsChecked="{Binding Using_Feat_4}"/>
    <CheckBox x:Name="cb_FEAT_5" Content="Feature 5" Margin="20,0,0,0" IsEnabled="{Binding Feature5Enabled}" IsChecked="{Binding Using_Feat_5}"/>
</StackPanel>

但是,它是片状而且不对。即使禁用了所有功能,_usingAllFeatures仍然为true。这两种方法都不起作用 - 我总是从被禁用的功能中获取错误,最终取消选择全部选择,实际上应该选择它。

我希望制作某种形式的解决方案,当检查“所有选定”状态时

bool? _usingAllFeatures = null;
// If any feature is offered but unselected, the return status will be false (All Features will be unselected).
public bool AllFeatures
{
    get
    {
        _usingAllFeatures =
            (Feature1Enabled ? Using_Feat_1 : _usingAllFeatures)
          & (Feature2Enabled ? Using_Feat_2 : _usingAllFeatures)
          & (Feature3Enabled ? Using_Feat_3 : _usingAllFeatures)
          & (Feature4Enabled ? Using_Feat_4 : _usingAllFeatures)
          & (Feature5Enabled ? Using_Feat_5 : _usingAllFeatures);

        //_usingAllFeatures =
        //    (FeatureCSPEnabled    && Using_Feat_CSPKSP)
        //  & (FeatureJSPEnabled    && Using_Feat_JSP)
        //  & (FeatureJCPROVEnabled && Using_Feat_JCPROV)
        //  & (FeatureSDKEnabled    && Using_Feat_SDK)
        //  & (FeatureSNMPEnabled   && Using_Feat_SNMP);

        return _usingAllFeatures.HasValue && _usingAllFeatures.Value;
    }
    set
    {
        Using_Feat_1 = value;
        Using_Feat_2 = value;
        Using_Feat_3 = value;
        Using_Feat_4 = value;
        Using_Feat_5 = value;

        OnPropertiesChanged("AllFeatures");
    }
}

我可能会过度思考这个并没有找到正确的解决方案......我错过了什么?

更新

OMG ......请帮我解决这个丑陋的野蛮人:

if checkbox is enabled
  and selected
    record "true" to _usingAllFeatures
  else if checkbox not selected
    record  "false" to _usingAllFeatures
else if checkbox not enabled
  do nothing

是的,它有效..但是......呃!请帮我一个更好的解决方案!

我正在阅读有关可观察集合和多层次模型的内容..但我不太了解如何将该方法与可以启用/禁用的复选框列表集成。

2 个答案:

答案 0 :(得分:1)

我认为,当选择某些功能(不是全部)时,问题的根源是AllFeaturesfalse的不一致状态,但在将属性设置为false时重置所有功能。

因此,我建议您从显示的状态中解除cb_FEAT_All上的用户互动:

实施get-only bool AllFeatures

public bool AllFeatures
{
    get
    {
        return
            (!Feature1Enabled || Using_Feat_1) &&
            (!Feature2Enabled || Using_Feat_2) /* ... more features */;
    }
}

通知功能

中的相应属性更改
private bool _Feature1Enabled;
public bool Feature1Enabled
{
    get { return _Feature1Enabled; }
    set
    {
        _Feature1Enabled = value;
        OnPropertiesChanged("Feature1Enabled");
        OnPropertiesChanged("AllFeatures");
    }
}

private bool _Using_Feat_1;
public bool Using_Feat_1
{
    get { return _Using_Feat_1; }
    set
    {
        _Using_Feat_1 = value;
        OnPropertiesChanged("Using_Feat_1");
        OnPropertiesChanged("AllFeatures");
    }
}
// more properties...

创建一个处理更改的方法:

void ExecToggleAllFeatures()
{
    var newVal = !AllFeatures;

    if (Feature1Enabled) Using_Feat_1 = newVal;
    if (Feature2Enabled) Using_Feat_2 = newVal;
    // ...
}

创建一个ICommand,将ExecToggleAllFeatures称为Execute处理程序。可以使用RelayCommand(如果您还不知道,可以谷歌)。

public ICommand ToggleCheckAllCommand { /* implement get/set */ }

绑定您的复选框:

<CheckBox x:Name="cb_FEAT_All" Content="Select All" IsChecked="{Binding AllFeatures,Mode=OneWay}" Command={Binding ToggleCheckAllCommand} Margin="6,0,0,0" />

可以也将ExecToggleAllFeatures的逻辑放入AllFeatures制定者,但我认为它不是一个好的设计选择(纯粹基于意见)< / p>

答案 1 :(得分:0)

几步:

使用MVVM框架。选择其中一个,我个人使用ReactiveUI

首先,将您的功能放在Feature类的对象列表中:

    public class Feature : ReactiveObject // this contains INotifyPropertyChanged and so on
    {
     public string Name
            {
                get { return _name; }
                set { this.RaiseAndSetIfChanged(ref _name, value); }
            }

     public bool Checked
            {
                get { return _checked; }
                set { this.RaiseAndSetIfChanged(ref _checked, value); }
            }

 public bool Enabled
            {
                get { return _enabled; }
                set { this.RaiseAndSetIfChanged(ref _enabled, value); }
            }
    }

在你看来:

<StackPanel Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Top">
    <Label x:Name="labelFeatures" Content="Features" FontWeight="Bold" />
    <CheckBox x:Name="cb_FEAT_All" Content="Select All" IsChecked="{Binding AllSelected}" Margin="6,0,0,0" />
<ListView ItemsSource="{Binding Features}">
<ListView.ItemTemplate>
<DataTemplate>
    <CheckBox Content="{Binding Name}" Margin="20,0,0,0" IsEnabled="{Binding Enabled}" IsChecked="{Binding Checked}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

现在,如果您想拥有更多功能,只需在列表中添加另一个对象即可。

现在我们将使用一些魔法形式的ReactiveUI,也许我会说服你使用Rx:)

在ViewModel构造函数中,我们必须创建列表:

    public class ViewModel : ReactiveObject
    {
        private bool _allSelected;

        public bool AllSelected
        {
            get { return _allSelected; }
            set { this.RaiseAndSetIfChanged(ref _allSelected, value); }
        }

        public ViewModel()
        {
            Features = new ReactiveList<Feature>
            {
                ChangeTrackingEnabled = true // this enabes few tricks
            };

            Features.ItemChanged.Where(x => x.PropertyName == nameof(Feature.Checked)).Subscribe(_ =>
            {
                // any time item in the list is checked, select/deselect all
                if (Features.All(x => x.Checked)) // feel free to filter out disabled features
                    AllSelected = true;
                else
                    AllSelected = false;
                // you can make AllSeleted nullable and make checkbox show "NotSpecified" state like in some installers, when not all, but some are selected
            });

            this.WhenAnyValue(x => x.AllSelected).Subscribe(x =>
            {
                // each time someone checks AllFeatures checkbox, check/uncheck all
                // x has the value of AllSelected
                using (Features.SuppressChangeNotifications()) // to prevent setting AllSelected to false after checking every item
                    foreach (var feature in Features)
                    {
                        feature.Checked = x;
                    }
            });

Features.Add(new Feature{Name="Feature 1", Enabled =  true, Checked = true}); // add all other features, maybe get them from database or from REST service, you name it

        }

        public ReactiveList<Feature> Features { get; set; }
    }