围绕mvvm编程模式我的头脑。绝对是一种不同的思维方式。
此时,我有一个简单的问题。虽然我当然可以“蛮力”一个解决方案,但我确信那里有一些优雅的东西,我只是想念它。
我有一个复选框列表,其中一些已启用,一些已禁用。这是怎么回事无关紧要。我还有一个Select All toggle。
我想要做的是,能够评估启用的功能,查看它们是否已被选中(确定是否显示“全部选择”作为选中)或选择所有启用的功能(忽略禁用的)然后在选择全部旁边显示一个检查。
就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
是的,它有效..但是......呃!请帮我一个更好的解决方案!
我正在阅读有关可观察集合和多层次模型的内容..但我不太了解如何将该方法与可以启用/禁用的复选框列表集成。
答案 0 :(得分:1)
我认为,当选择某些功能(不是全部)时,问题的根源是AllFeatures
为false
的不一致状态,但在将属性设置为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; }
}