用于复合条件参数的分组可勾选菜单
一种基于菜单的用户界面筛选解决方案,其中第一级显示筛选条件,后续级别以越来越细粒度的分辨率选项提供可选值。
引言
处理大型数据集通常需要某种形式的筛选机制。我曾多次发现以下筛选方法(对 UI 和代码设计都很有用)派上用场。我的目标是实现一种基于菜单的用户界面筛选解决方案,其中第一级显示筛选条件,后续级别以越来越细粒度的分辨率选项提供可选值。每个条件**只能有一个**筛选值或**没有**筛选值,其中“没有”表示不进行筛选。我还希望有一个视觉指示器来显示是否正在应用任何筛选*。
以下是一些可能使它更清楚的屏幕截图
* 了解我的目标的一个更快的方法是下载并运行示例项目……
背景
UI 方面
使用菜单作为筛选参数选择平台,其优点是占用最少的客户区域,同时向用户提供有关筛选条件的详细信息(随着菜单展开,信息越详细)。
实现
建议的实现 fortemente 基于**附加属性**。这允许在视图中实现所有功能,并且仅通过**绑定**将相关信息传递给 ViewModel。在 ViewModel 中,例如,使用筛选/搜索命令和 LINQ/lambda,可以为显示填充结果绑定的列表(在此示例中未实现)。
附加属性 (AP)
下一段是为不熟悉**附加属性**的读者准备的。如果您熟悉此概念,可以跳到“使用代码”部分。
这里有一段引述
“附加属性 (AP) 的概念是 WPF 最具创新性的功能之一,也是利用率最低的功能之一。”
--WPF Control Development Unleashed/SAMS
我将尝试通过一个“真实生活”的示例来传达 AP 的概念,而不是解释 WPF 中的 AP 是什么(您可以在任何 WPF 书籍中找到)。
想象一个定制的办公桌互联网订单表单,其中有几行可以让用户指定其属性。
Color
宽度
高度
深度
底部有一个提交按钮,按下该按钮会发送此表单,并因此会构建(实例化)一张新桌子。
一切都很好,除了……您想要一些抽屉……
在“启用了 AP 的现实”中,您需要做的就是拿出您的“魔法笔”并写下另一行。现在表单看起来像这样
颜色
-绿色宽度
-100高度
-50深度
-50抽屉数量
抽屉数量
的默认值为零,因此如果您希望桌子没有抽屉,可以省略其值。
我们在这里实际做的是**对现有的“初始化表单”进行了修改/定制/扩展**。
为了让事情更有趣——假设我们希望高度
值随着我们在抽屉数量
属性中设置的每个额外抽屉而增加 20 厘米。幸运的是,就像为抽屉数量
属性设置默认值一样,我们也可以设置一个回调,当其值发生变化时就会触发。在那里,我们可以像这样更新高度
属性
Height=_BaseHeight+(NumberOfDrawers*20)
我希望这个小比喻能帮助您初步了解 AP 在 WPF 中的作用。
我发现附加属性在两种情况下特别有用
- 功能扩展。
- 为非
依赖属性
的属性启用**绑定/动画**;在那里,AP(它们是**可绑定的**/**可动画的**,因为它们是依赖属性
)充当代理。
使用代码
为了实现所需的功能,我使用 AP 扩展了MenuItem
元素。
在 XAML 中
每个条件顶层MenuItem
都设置了IsGroupValueHolder
AP 为True
(用于代码实现目的),并将Value
设置为绑定的 ViewModel 的Matching
属性。
<MenuItem Header="Rating" local:CheckGroupHelper.IsGroupValueHolder="True"
Style="{StaticResource CheckGroupChildStyle}"
local:CheckGroupHelper.Value=
"{Binding Rating,Mode=OneWayToSource}">
每个条件底层可勾选值MenuItem
都将Value
设置为某个预定义参数。
<MenuItem Header="5" Style="{StaticResource CheckGroupChildStyleLeaf}" local:CheckGroupHelper.Value="5">
在样式内部
'CheckGroupChildStyleLeaf
' - **IsCheckable
** 设置为True
(允许用户勾选),**IsChecked
** 通过绑定设置为我们自己的**IsCheckGroupChecked
AP**(我们监控其值更改)。
'CheckGroupChildStyle
' - IsCheckable
设置为**False
**,星号可见性由**绑定**到**Value
** AP 控制,使用**AnyString2Asterisk
** 转换器。
在代码中(CheckGroupHelper
)
在IsCheckGroupCheckedChanged
中(仅对“叶子”MenuItem
相关)
如果勾选
- 取消勾选此条件组中当前勾选的值(这将导致一个空值冒泡到组的值持有者)。
- 冒泡此“叶子”值。
MenuItem micc = GetGroupCurrentlyCheckedLeaf(mi);
if (micc != null)
{
micc.IsChecked = false;
}
mi.Parent.SetValue(CheckGroupHelper.ValueProperty,
obj.GetValue(CheckGroupHelper.ValueProperty));
如果未勾选:冒泡空值。
mi.Parent.SetValue(CheckGroupHelper.ValueProperty, "");
在ValueChanged
中
- 将值冒泡到
MenuItem
树的上方。 - 不要将空值冒泡到条件顶层
MenuItem
(IsGroupValueHolder =True
)之外,如果任何其他条件顶层MenuItem
有一个值(因此,“Filters”MenuItem
仍然显示它有一个值!)。
关注点
- 我希望通过这个简单的演示,我已成功展示了如何使用
AttachedProperty
s,以最少的代码实现特殊的 UI 功能,同时保持干净的 MVVM 基础结构。 - 在此应用程序的早期版本中,我使用了“命名组”方法(就像单选按钮的
GroupName
属性一样)。
这种方法虽然提供了很大的设计灵活性,但对于这种情况来说是冗余的。这里的组是根据MenuItems
的层次结构聚集和定义的。