WPF 的饼图菜单






4.78/5 (21投票s)
为 WPF 创建饼图菜单控件。
引言
饼状菜单的概念已经存在一段时间了,并且在某些情况下可能比标准的线性菜单更有优势。特别是在像 Microsoft PixelSense/Samsung SUR40 这样的大型触摸屏变得越来越普及的时候,我们可能会期待对线性菜单的替代方案的需求增加。
不幸的是,.NET 框架没有为 WPF 提供标准的饼状菜单控件。最接近的是 Surface 2.0 SDK 的 ElementMenu
控件。本文介绍了一个用于 WPF 的饼状菜单控件。由于该菜单旨在用于支持触摸的应用程序,因此它会响应鼠标和触摸事件。
Using the Code
PieMenu
的使用方式类似于 WPF 的 Menu
控件;一个 PieMenu
可以包含任意数量的 PieMenuItem
,而这些 PieMenuItem
又可以包含 PieMenuItem
,深度不受限制。菜单项的标签由 Header
属性提供,其行为由 Click
事件或 Command
属性指定。
<p:PieMenu x:Name="Menu1">
<p:PieMenuItem Header="Item 1">
<p:PieMenuItem Header="Item 1.1" Click="PieMenuItem_Click"/>
<p:PieMenuItem Header="Item 1.2" Command="NotACommand"/>
<p:PieMenuItem Header="Item 1.3" />
</p:PieMenuItem>
<p:PieMenuItem Header="Item 2">
<p:PieMenuItem Header="Item 2.1" />
<p:PieMenuItem Header="Item 2.2" />
</p:PieMenuItem>
<p:PieMenuItem Header="Item 3">
<p:PieMenuItem Header="Item 3.1" />
<p:PieMenuItem Header="Item 3.2" />
<p:PieMenuItem Header="Item 3.3" />
<p:PieMenuItem Header="Item 3.4" />
</p:PieMenuItem>
</p:PieMenu>
这个菜单,仅使用默认设置,将看起来像这样(当 Item 1.1 被按下时)
所有 UI 控件通用的属性,例如 Background
、Foreground
、FontSize
等,都按预期工作,并且在菜单项中指定的属性会覆盖在菜单中指定的属性。此外,PieMenu
具有几个自定义属性
SelectedBackground
指定选定菜单项的颜色(默认gray
)Radius
指定菜单第一级的半径(默认50.0
)InnerRadius
指定菜单中间孔的半径(默认10.0
)Gap
指定菜单各级之间的距离(默认5.0
)SectorGap
指定同一级别菜单项之间的距离(默认5.0
)MenuSector
指定菜单将使用的圆弧扇形(默认360.0
)Rotation
指定放置第一个菜单项的角度(默认0.0
)RotateText
指定菜单项的标签是否旋转以跟随圆圈(默认True
)
PieMenuItem
具有一个自定义属性:SubMenuSector
,用于指定其子项将占据的圆弧扇形(默认 120.0
)。
因此,我们可以如下定义一个饼状菜单
<p:PieMenu x:Name="Menu2"
Background="Blue"
SelectedBackground="LightBlue"
Foreground="White"
BorderBrush="Red"
SectorGap="10"
Gap="10"
Radius="100"
InnerRadius="0"
RotateText="False"
Rotation="90"
MenuSector="180">
<p:PieMenuItem Header="Item 1"
BorderThickness="1"
SubMenuSector="90">
<p:PieMenuItem Header="Item 1.1" />
<p:PieMenuItem Header="Item 1.2" />
<p:PieMenuItem Header="Item 1.3" />
</p:PieMenuItem>
<p:PieMenuItem Header="Item 2"
BorderThickness="2"
BorderBrush="Green"
SubMenuSector="60">
<p:PieMenuItem Header="Item 2.1" />
<p:PieMenuItem Header="Item 2.2" />
</p:PieMenuItem>
<p:PieMenuItem Header="Item 3"
FontFamily="Times New Roman"
FontStyle="Italic"
FontSize="14"
Background="Green"
Foreground="Black" >
<p:PieMenuItem Header="Item 3.1" />
<p:PieMenuItem Header="Item 3.2" />
<p:PieMenuItem Header="Item 3.3" />
<p:PieMenuItem Header="Item 3.4" />
</p:PieMenuItem>
</p:PieMenu>
这将得到以下(不是很好看)的菜单(当 Item 2.1 被按下时)
关注点
PieMenu
类扩展了 ItemsControl
,而 PieMenuItem
类扩展了 HeaderedItemsControl
。这些通用控件为我们提供了很多关于菜单项管理的便利。
在菜单的实现中,PieMenu
类负责整个菜单的渲染以及维护菜单的状态。另一种方法是让每个 PieMenuItem
实例自行渲染并维护自己的状态。选择的、集中的方法的好处是,与让每个菜单项计算自己的轮廓和放置位置相比,它更容易计算菜单项的轮廓和相对位置。这种方法的缺点是它需要一定量的簿记来记住哪些菜单项被选中和可见,以及确定哪个菜单项被点击。在这最后一点上,一种分散的方法,即每个菜单项跟踪自己的状态并处理输入事件,可能会更简单,即使需要在菜单项之间进行一些消息传递以维护菜单的正确全局状态。
致谢
此代码是在 BRIDGE 和 DARIUS 项目的背景下开发的。
历史
- 2013 年 1 月 5 日:原始版本