65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 的饼图菜单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (21投票s)

2013年1月5日

CPOL

3分钟阅读

viewsIcon

60766

downloadIcon

2912

为 WPF 创建饼图菜单控件。

引言

饼状菜单的概念已经存在一段时间了,并且在某些情况下可能比标准的线性菜单更有优势。特别是在像 Microsoft PixelSense/Samsung SUR40 这样的大型触摸屏变得越来越普及的时候,我们可能会期待对线性菜单的替代方案的需求增加。

不幸的是,.NET 框架没有为 WPF 提供标准的饼状菜单控件。最接近的是 Surface 2.0 SDKElementMenu 控件。本文介绍了一个用于 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 控件通用的属性,例如 BackgroundForegroundFontSize 等,都按预期工作,并且在菜单项中指定的属性会覆盖在菜单中指定的属性。此外,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 实例自行渲染并维护自己的状态。选择的、集中的方法的好处是,与让每个菜单项计算自己的轮廓和放置位置相比,它更容易计算菜单项的轮廓和相对位置。这种方法的缺点是它需要一定量的簿记来记住哪些菜单项被选中和可见,以及确定哪个菜单项被点击。在这最后一点上,一种分散的方法,即每个菜单项跟踪自己的状态并处理输入事件,可能会更简单,即使需要在菜单项之间进行一些消息传递以维护菜单的正确全局状态。

致谢

此代码是在 BRIDGEDARIUS 项目的背景下开发的。

历史

  • 2013 年 1 月 5 日:原始版本
© . All rights reserved.