菜单

  WPF 提供了两个菜单控件:Menu(用于主菜单)和 ContextMenu(用于关联到其他元素的弹出菜单)。与所有 WPF 类一样,WPF 负责呈现 Menu和 ContextMenu 控件。这意味着这些控件不是简单的 Windows 库封装器,用户可获得更大的灵活性,包括在浏览器中驻留的应用程序中使用它们。

  WPF 没有假定单独的菜单应放在何处。通常使用DockPanel或Grid 面板的顶行将菜单停靠在窗口顶部,并将它拉伸到整个窗口的宽度。然而,可将菜单放在任何地方,甚至放到其他控件的边缘。甚至,还可在窗口中添加任意数量的菜单。尽管这可能不很合理,但可以堆栈菜单栏或在整个用户界面中分散布置。

  Menu 类添加了新属性 IsMainMenu,当该属性为 true 时(这是默认值),按下 Alt 键或 F10键时菜单就会获得焦点,就像其他所有 Windows 应用程序一样。除这个细节外,Menu 容器还可以使用几个熟悉的 ItemsControl 属性。这意味着可使用 ItemsSource、DisplayMemberPath、ItemTemplate 以及 ItemTemplateSelector 属性,创建使用数据绑定的菜单。还可以应用分组,改变菜单项在菜单中的布局以及为菜单项应用样式。

菜单项

  菜单由MenuItem对象和Separator对象构成。因为每个菜单项都有标题(包含用于菜单项的文本),并且包含MenuItem对象的集合(代表子菜单),所以MenuItem类继承自HeaderedItemsControl类。Separator对象只显示一条分隔菜单项的水平线。

下面是一个简单的MenuItem对象组合,用于创建下图所示的基本菜单结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Grid>
<Menu>
<MenuItem Header="File">
<MenuItem Header="New" />
<MenuItem Header="Open" />
<MenuItem Header="Save" />
<Separator />
<MenuItem Header="Exit" />
</MenuItem>
<MenuItem Header="Edit">
<MenuItem Header="Undo" />
<MenuItem Header="Redo" />
<Separator />
<MenuItem Header="Cut" />
<MenuItem Header="Copy" />
<MenuItem Header="Paste" />
</MenuItem>
</Menu>
</Grid>

  WPF 允许违反构造菜单的大多数常识性规则。例如,可在Menu或 Menultem 对象中包含非 MenuItem 对象,从而可以创建包含普通 WPF元素的菜单,从普通的CheckBox 控件到DocumentViewer 控件。有许多理由说明,在菜单中放置非 Menultem 对象几乎总是一种错误方式。如果在菜单中放置非 MenuItem对象,将会显示古怪的内容,需要加以跟踪和纠正。例如,只要将鼠标移出 Menultem 对象的边界,MenuItem 中的 TextBox 控件就会丢失焦点。如果确实希望用户界面包含一些具有控件的下拉菜单,可考虑使用另一个元素(如 Expander 控件),并设置该元素的样式使其符合需要。只有当确实希望具有菜单行为时才使用菜单–一组能够被单击的命令。

如果希望在用户单击其他位置之前始终显示打开的子菜单,可将Menultem.StaysOpenOnClick属性设置为 tmue。

  当单击 Menultem 对象时,为进行响应,可选择处理 Menultem.Click 事件。可为单个菜单项处理该事件,或将事件处理程序关联到Menu根标签。另一个选择是使用Command、CommandParameter 以及CommandTarget 属性,将 Menultem 连接到 Command 对象。如果用户界面包含使用相同命令的多个菜单(例如主菜单和上下文菜单),或者包含菜单和执行相同工作的工具栏,这种方法是非常有用的。

  除通过Header属性提供的文本内容外,MenuItem对象实际上还可以显示其他几个细节:

  • 在菜单命令左边的页边距区域显示小图标
  • 在页边距区域显示复选标记。如果同时设置复选标记和图标,将只显示复选标记
  • 在菜单文本的右边显示快捷键。例如,可看到指示Open命令快捷键的Ctrl + O
1
2
3
4
<MenuItem
Header="Open"
Icon="👀"
InputGestureText="Ctrl + O" />

有几个有用的属性可以指示 MenuItem 对象的当前状态,包括IsChecked、isHighlightedIsPressed 以及 IsSubmenuOpen。可使用这些属性编写触发器,触发器应用不同样式来响应特定操作。

ContextMenu类

  与 Menu 类一样,ContextMenu 类也包含 Menultem 对象的集合。区别是 ContextMenu 对象不能放置在窗口中,只能被用于设置其他元素的ContextMenu 属性:

1
2
3
4
5
6
7
8
9
10
11
12
<TextBox
Height="30"
VerticalContentAlignment="Center"
Width="100">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" />
<MenuItem Header="Paste" />
<MenuItem Header="Cut" />
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>

  ContextMenu 属性是在 FrameworkElement 类中定义的,所以几乎所有 WPF 元素都支持该属性。如果为通常具有自己的上下文菜单的元素设置 ContextMenu 属性,设置的菜单将会代替标准的菜单。如果只是希望移除已经存在的上下文菜单,那么可以将该属性设置为空引用。

  如果已将 ContextMenu对象关联到元素,那么当用户右击控件(或当控件具有焦点时按下Shif+F10 组合键)时,会自动显示与其关联的 ContextMenu 对象。如果元素的 IsEnabled 属性被设置为 false,就不会显示上下文菜单,除非使用 ContextMenuService.ShowOnDisabled 附加属性明确指示允许显示:

1
2
3
4
5
6
7
8
<TextBox
ContextMenuService.ShowOnDisabled="True">
<TextBox.ContextMenu>
<ContextMenu>
...
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>

菜单分隔条

  Separator 对象是将菜单分成相互关联的命令组的标准元素。然而,分隔条的内容是完全可变的,这得益于控件模板。通过使用分隔条并提供新的模板,可为菜单添加其他不能被单击的元素,例如副标题。

  可能希望通过简单地为菜单添加非 MenuItem对象来添加副标题,例如包含一些文本的TextBlock对象。然而,如果使用这个步骤,新添加的元素会保持菜单选择行为。这意味着当通过键盘移动到该元素上,以及当将鼠标悬停在该元素上时,其边缘会发出蓝色辉光。Separator对象不具有这种行为——它是一块固定内容,不响应键盘和标操作。

下面是定义文本挑剔的Separator对象的一个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Separator>
<Separator.Template>
<ControlTemplate>
<Border
Background="PaleGoldenrod"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2"
Padding="3">
<TextBlock FontWeight="Bold">Editing Commands</TextBlock>
</Border>
</ControlTemplate>
</Separator.Template>
</Separator>

  但 Separator对象不是内容控件,所以不能从希望使用的格式化中分离出希望显示的内容(如文本字符串)。这意味着如果希望改变它的文本,在每次使用分隔条时就必须定义相同的模板。为简化这个过程,可创建分隔条样式,将希望设置的 TextBlock 对象(位于 Separator 对象内部)的所有属性(文本除外)捆绑在一起。

工具栏和状态栏

  工具栏和状态栏是 Windows 领域两个早已存在的重要内容。这两个控件都是包含项集合的特殊容器。通常,工具栏包含按钮,而状态栏主要包含文本和其他非交互的指示器(如进度条)。然而,工具栏和状态栏都可以使用各种不同的控件。

  在 Windows 窗体中,工具栏和状态栏有各自的内容模型。尽管也能使用封装器在工具栏和状态栏中随意放置控件,但这个过程并非完美无缺。WPF中的工具栏和状态栏没有这一限制。它们支持 WPF 内容模型,从而可为工具栏和状态栏添加任何元素,得到无可比拟的灵活性。实际上,没有特定于工具栏的元素,也没有特定于状态栏的元素。需要的所有内容就是 WPF 基本元素的集合。

ToolBar控件

  典型的 ToolBar 控件充满了 ButtonComboBoxCheckBoxRadioButton 以及 Separator 对象。因为这些元素都是内容控件(Separator 元素除外),所以可在它们内部放置文本和图像。尽管可使用其他元素(如 Label对象和 Image 对象)在 ToolBar 控件中放置不能交互的元素,但这常令人感到闲惑。

  ToolBar 控件不但改变了它所包含的许多控件的外观,还改变了 ToggleButton 控件以及继承自 ToggleButton 的 CheckBox 和 RadioButton 的行为。在 ToolBar 控件中呈现的 ToggleButton 或CheckBox看来起像普通按钮,但当单击时,按钮会保持突出显示(直到再次单击)。RadioButton具有类似的外观,但为了清除突出显示,必须单击同一组中的另一个 RadioButton(为防止混乱,最好总在工具栏中使用 Separator对象将一组 RadioButton 对象隔开)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ToolBar>
<Button>Download</Button>
<CheckBox FontWeight="Bold">Bold</CheckBox>
<CheckBox FontStyle="Italic">Italic</CheckBox>
<CheckBox>
<TextBlock TextDecorations="Underline">Underline</TextBlock>
</CheckBox>
<Separator />
<ComboBox SelectedIndex="0">
<ComboBoxItem>100%</ComboBoxItem>
<ComboBoxItem>50%</ComboBoxItem>
<ComboBoxItem>25%</ComboBoxItem>
</ComboBox>
<Separator />
</ToolBar>

  ToolBar 控件有一些奇特之处。首先,和其他继承自ItemsControl类的控件不同,它没有提供专门的封装器类(换句话说,没有 ToolBarltem 类)。ToolBar 控件并不像其他列表控件那样需要这样的封装器来管理项和跟踪选择等。ToolBar 控件的另一个奇特之处是它继承自Headered[temsControl类,尽管 Header 属性不起作用,但可通过一些有趣的方式使用这个属性。例如,如果具有使用几个 ToolBar 对象的界面,可允许用户从上下文菜单中选择使用哪个 ToolBar 对象。在这个菜单中,可使用在 Header属性中设置的工具栏名称。

  **ToolBar 控件还有一个更有趣的属性:Orientation。可通过将 ToolBar.Orientation 属性设置为 Vertical,从而创建停靠到窗口侧边的竖向工具栏。**然而,除非使用 LayoutTransform 旋转元素,否则工具栏中的每个元素仍是水平方向(例如,文本不会侧转)。

溢出菜单

  如果工具栏具有的内容比在窗口中能够容纳的内容更多,那么工具栏会移出项直到内容合适为止。这些额外的项被放到溢出菜单中,通过单击工具栏末尾的下拉箭头可以看到这个溢出菜单。下图显示的工具栏和上图显示的工具栏相同,但是由于窗口更小,因而需要使用溢出菜单。

  ToolBar 控件自动向溢出菜单添加项,顺序是从最后一个项开始。然而,可通过为工具栏中的项应用 ToolBar,OverflowMode 附加属性,在一定程度上配置这一行为的工作方式。可使用OverflowMode.Never 模式确保重要的项永远不会被放到溢出菜单中,当空间不足时,OverflowMode.AsNeeded(默认值)模式允许项被放到溢出菜单中,而 OverflowMode.Always 模式总是强制项永远保留在溢出菜单中。

  如果工具栏包含多个 OverflowMode.AsNeeded 项,ToolBar 控件会首先移除工具栏末尾的项但无法为工具栏中的项指定相对的优先权。例如,无法创建一个项,只有当其他能够被重新定位的项已经被移走之后,才允许将该项移动到溢出菜单中。也无法创建能根据可用空间调整大小的按钮。

ToolBarTray类

  尽管可向窗口中自由地添加多个 ToolBar 控件,并使用布局容器管理它们,但 WPF 还是提供了 ToolBarTray 类,专门用于完成一部分工作。本质上,ToolBarTray 类包含 ToolBar 对象的集合(通过 ToolBars 属性提供该集合)。

  ToolBarTray 类使得工具栏共享同一行或同一栏更加容易。可配置 ToolBarTray 对象,从而使某些工具栏共享一栏,而其他工具栏被放置到其他栏中。TooIBarTray对象为整个 ToolBar 区域提供了阴影背景。但最重要的是,ToolBarTray类增加了对工具栏拖放功能的支持。除非将ToolBarTray.IsLocked 属性设置为 true,否则用户可在 ToolBar 托盘中通过单击左边的手柄,重新排列工具栏。工具栏可在同一栏中重新定位或移到不同的栏中。然而,用户不能将工具栏从一个 ToolBarTray 对象拖动到另一个 ToolBarTray 对象中。如果希望锁定单个工具栏,只需要为恰当的 ToolBar 对象简单地设置 Too1BarTray.IsLocked 附加属性。

当移动工具栏时,一些内容可能会被隐藏掉。例如,用户可将一个工具栏移动到某个位置,从而使相邻的工具栏只有很少的空间。这时,丢失的项会被添加到溢出莱单中

  可在 ToolBarTray 对象中放置所需的任意数量的 ToolBar 对象。默认情况下,所有工具栏都会以从左向右的顺序放置在最顶部的栏中。最初,每个工具栏会得到所需的全部宽度(如果剩余的空间无法满足后续添加的工具栏的需要,包含的某些或全部按钮就会被移动到溢出菜单中)。为更好地加以控制,可使用数字索引设置 Band 属性,确定工具栏应当被放置到哪一栏中(最顶部的栏的索引是 0)。还可通过使用 BandIndex 属性明确地设置在一栏中的什么位置放置工具栏。将 BandIndex属性设置为0,会将工具栏放在一栏的开始位置。

下面是一些简单的标记,这些标记在一个 ToolBarTray 对象中创建了几个工具栏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<ToolBarTray>
<ToolBar>
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ToolBar>
<ToolBar>
<Button>A</Button>
<Button>B</Button>
<Button>C</Button>
</ToolBar>
<ToolBar Band="1">
<Button>Red</Button>
<Button>Blue</Button>
<Button>Green</Button>
<Button>Black</Button>
</ToolBar>
</ToolBarTray>

StatusBar控件

  StatusBar 控件不如 ToolBar 那样富有力。与 TooIBa 控件一样,StatusBar 可以包含任何内容(内容被隐式封装到 StatusBartem 对象中),并且重写了一些元素的默认样式,使它们更适合呈现。然而,StatusBar控件不支持拖动式的重新排列,也不支持溢出菜单,主要用于显示文本和图像指示器(并且有时用于显示进度条)。

  如果希望使用继承自ButtonBase类的元素或ComboBox控件,StatusBar控件就不能很好地工作。不能重写这些控件的样式,从而在状态栏中它们看起来很不和谐。如果需要创建包含这些控件的状态栏,可考虑在窗口的底部停靠普通的 TooIBar 控件。可能正是因为缺少这一特征,所以 StatusBar 类位于 System.Windows.Controls.Primitives 名称空间中,而不是位于更常见的System.Windows.Controls 名称空间中,而 ToolBar控件位于 System.Windows,Controls 名称空间中。

  如果使用状态栏,有一点应予注意。StatsBar控件通常使用水平的:StackPanel 面板,从左向右地放置它的子元素。然而,应用程序常使用按比例设置尺寸的状态栏项,或将某些项保持锁定在状态栏的右边。可使用1temsPanelTemplate 属性指示状态栏使用不同的面板来实现这种设计。

  获得按比例改变尺寸或右对齐项的一种方法是为布局容器使用Grid 面板。唯一的技巧是必须在 StatusBarltem 对象中封装子元素,从而能正确地设置 Grid.Column 属性。下面的示例使用 Grid 面板在状态栏的左边放置一个 TextBlock 元素,在右边放置另一个 TextBlock 元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StatusBar Grid.Row="1">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<TextBlock>Left Side</TextBlock>
<StatusBarItem Grid.Column="1">
<TextBlock>Right Side</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>

  此处强调了 WPF 的一项重要优点–其他控件可受益于核心布局模型,而不用重新创建它们。相比之下,Windows 窗体提供了几个能封装某些类型的按比例改变尺寸的项的控件,包括StatusBar 和 DataGridView。尽管概念上是这样的,但这些控件为了管理子元素,必须包含它们自己的布局模型,并添加它们自己的特定布局属性。在 WPF中,情况并非如此–每个继承自ItemmsControl类的控件都可以使用任何面板来安排它的子元素。

功能区

添加功能区

  要使用功能区,首先创建新的WPF项目,并添加对RibbonControlsLibrary.dll 程序集的引用。可在类似于 Program Files (x86)\Microsoft Ribbon for WPF\V4.0 的文件夹中找到它。
  集映射到XML前缀:与其他所有不是WPF核心库一部分的控件相同,在使用功能区控件之前需要将控件程序

1
xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"

在高版本中,Ribbon已经内置了,不需要额外引入

  RibbonWindow 类,该类从 Window 继承而来,但提供了附加的功能区集成功能。最重要的是,RibbonWindow 类为快速访问工具栏(显示在功能区上方的自定义的常用按钮组)提供了一个位置,还提供了一个位置来放置上下文功能区选项卡(只为某些任务显示的选项卡)的标题。

<RibbonWindow
…>


1
2
3
4
5
6
7
public partial class MainWindow : RibbonWindow
{
public MainWindow()
{
InitializeComponent();
}
}

  功能区控件实际上包含三部分:快速访问工具栏(位于顶部)、应用程序菜单(该部分通过最左边的按钮提供,在所有选项卡之前)和多选项卡的功能区控件自身。

应用程序菜单

  开始使用功能区的最简单方法是填充应用程序菜单。应用程序菜单基于两个简单的类:RibbonApplicationMenu(该类继承自MenuBase)和 RibbonApplicationMenuItem(该类继承自Menultem)。Ribbon 使用 WPF 控件基类,并且派生出更专业的版本。从纯粹主义者的角度看,ToolBar 和 StatusBar 使用更整洁的模型,因为它们能使用标准的 WPF 控件,它们只是简单地对标准 WPF 控件的样式重新设置。但功能区需要额外的一层派生类,从而支持功能区控件的许多高级特性。例如,为支持RibbonCommand,RibbonApplicationMenu 和 RibbonApplicationMenuItem 类得到的增强超出了普通菜单类的功能

  为创建菜单,创建一个新的 RibbonApplicationMenu对象,并使用该对象设置Ribbon.Application-Menu 属性。正如您可能已经期望的,RibbonApplicationMenu 提供了 RibbonApplicationMenultem对象集合,每个 RibbonApplicationMenultem 对象代表一个可单击的菜单项。

下面的基本示例使用三个菜单项创建了一个应用程序菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Ribbon>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="Images/imac.png">
<RibbonApplicationMenuItem
Header="New"
ImageSource="Images/new.png"
ToolTip="Create a new document" />
<RibbonApplicationMenuItem
Header="Open"
ImageSource="Images/open.png"
ToolTip="Open a new document" />
<RibbonApplicationMenuItem
Header="Save"
ImageSource="Images/save.png"
ToolTip="Save the document with a new name" />
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
</Ribbon>

  处理功能区菜单的单击的方式与处理普通菜单的单击的方式相同。您可以响应 Click 事件,也可以使用 Command、CommandParameter和 CommandTarget 属性链接命令。命令特别适用于功能区菜单,原因是您可能将同一个命令链接到功能区菜单项和功能区按钮。

  另外值得注意的是,所有 RibbonApplicationMenutem对象都可以进一步包含多个 RibbonApplicationMenultem 对象以创建子菜单,如下图所示。每个子菜单项支持相同的文本、图像和工具提示选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<Ribbon>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="Images/imac.png">
<RibbonApplicationMenuItem
Header="New"
ImageSource="Images/new.png"
ToolTip="Create a new document" />
<RibbonApplicationMenuItem
Header="Open"
ImageSource="Images/open.png"
ToolTip="Open a new document" />
<RibbonApplicationMenuItem
Header="Save"
ImageSource="Images/save.png"
ToolTip="Save the document with a new name">
<RibbonApplicationMenuItem Header="Save" ImageSource="Images/save.png" />
<RibbonApplicationMenuItem Header="Save As" ImageSource="Images/save.png" />
</RibbonApplicationMenuItem>
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
</Ribbon>

  为了分离菜单项,可通过在菜单中放置 RibbonSeparator 控件来添加水平分隔细线。如果要进一步完善,可用更多信息(最近打开的文档列表)填充下拉菜单面板的第二列,而且可在页脚区域添加更多详情(如指向帮助页面的链接)。这些区域的行为与内容控件类似,您只需要使用任意元素设置 Ribbon.AuxiliaryPaneContent 来填充右边的列,设置 Ribbon.FooterPaneContent 来填充页脚区域。与任意内容控件一样,这些内容属性可以包含具有各种交互元素的布局容器,也可以提供数据对象,然后由模板(通过 AuxiliaryPaneContentTemplate 和 FooterPaneContent 模板提供)加以解释。

选项卡、组与按钮

  功能区使用与填充应用程序菜单相同的模型来填充工具栏选项卡,只使用几个附加层。首先,功能区包含选项卡集合。然后每个选项卡包含一个或多个组,组是一块具有轮廓的、具有标题的、类似方框的区域。最后,每组包含一个或多个功能区控件。下图显示了这种布置。

选项卡、组合按钮

  这些要素中的每一个都有相应的类。首先声明恰当的RibbonTab 对象,使用 RibbonGroup 对象填充每个 RibbonTab 对象,并在每个组中放置 Ribbon控件(例如简单的 RibbonButton)。

  为功能区新建选项卡时,使用 Header属性来提供在选项卡中显示的文本(在功能区上方)。在选项卡中创建组时,使用 Header 属性来提供显示在功能区相应区域下方的文本。也可以使用SmallmageSource 来设置图像,如果空间有限的话,将组折叠为单个按钮。

下面的标记为功能区提供Home选项卡,其中包含Clipboard组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<Ribbon>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="Images/imac.png">
<RibbonApplicationMenuItem
Header="New"
ImageSource="Images/new.png"
ToolTip="Create a new document" />
<RibbonApplicationMenuItem
Header="Open"
ImageSource="Images/open.png"
ToolTip="Open a new document" />
<RibbonApplicationMenuItem
Header="Save"
ImageSource="Images/save.png"
ToolTip="Save the document with a new name">
<RibbonApplicationMenuItem Header="Save" ImageSource="Images/save.png" />
<RibbonApplicationMenuItem Header="Save As" ImageSource="Images/save.png" />
</RibbonApplicationMenuItem>
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
<RibbonTab Header="Home">
<RibbonGroup Header="Clipboard">
<RibbonButton
Label="Cut"
LargeImageSource="Images/cut.png"
SmallImageSource="Images/cut.png" />
<RibbonButton
Label="Copy"
LargeImageSource="Images/copy.png"
SmallImageSource="Images/copy.png" />
<RibbonButton
Label="Paste"
LargeImageSource="Images/paste.png"
SmallImageSource="Images/paste.png" />
</RibbonGroup>
</RibbonTab>
</Ribbon>

SmallImageSource 属性设置在使用小尺寸(标准 96 dpi 显示器上的 16x16 像素)显示项时使用的图像。LargelmageSource 属性设置在使用大尺寸(标准 96 dpi 显示器上的 32x32 像素)显示项时使用的图像。为避免不同像素密度下的伸缩缺陷,可用 DrawingImage 替代每幅图像的位图。

  在这个示例中,功能区完全由 RibbonButton对象构成,这是最常见的功能区控件类型。然而,WPF还提供了更多的几个选项,下表列出了这些选项。与应用程序菜单一样,大部分Ribbon 类继承自标准的 WPF 控件。它们只是在顶部添加更多功能区功能。例如,它们都包括Label属性,这样就允许在控件旁边添加文本标题(直接显示在大按钮之下,小按钮的右边,文本框或组合框的左边,依此类推)。

Ribbon控件类
名称 说明
RibbonButton 可单击的包含文本与图像的按钮,是功能区上最常见的要素
RibbonCheckBox 可选中或取消选中的复选框
RibbonRadioButton 一组互斥选中中的可单击选项(与普通的选项按钮类似)
RibbonToggle 具有两个状态的按钮:按下状态和取消按下状态。例如,许多程序使用这种按钮打开或关闭字体特性,如粗体、斜体和下划线
RibbonMenuButton 可弹出打开菜单的按钮。可通过使用RibbonMenuButton.Items集合的MenuItem对象来填充菜单
RibbonSplitButton 与 RibbonDropDownButton类似,但按钮实际上被分成两部分。用户可单击顶部(具有图片)来运行命令,或单击底部(具有文本和下拉箭头)来显示关联的项菜单。例如,Word中的 Paste 命令就是 RibbonSplitButton
RibbonComboBox 在功能区中嵌入组合框,用户可使用该控件键入文本或进行选择,就像是标准的ComboBox控件
RibbonTextBox 在功能区中嵌入文本框,用户可使用该控件键入文本,就像是标准的TextBox控件
RibbonSeparator 在功能区的各个控件(或控件组)之间绘制一条竖线,或在菜单的项之间绘制水平线

富工具提示

  功能区支持增型的工具提示模型,该模型显示更详细的弹出工具提示,可包括标题、说明信息以及图像(还可能包括页脚)。但所有些细节都是可选的,您只需要设置需要使用的细节。

  如果想要使用增强的工具提示,只需要删除标准的TooITip属性,并组合使用下表中描述的工具提示属性。可在任意功能区控件(包括RibbonButon)和RibbonApplicationMenultem对象上进行设置。唯一的限制是不能将实际元素(如链接)放入工具提示中,只限于文本和图像内容。

增强的工具提示属性

|名称|说明|
|ToolTipTitle|显示在该项工具提示顶部的标题|
|ToolTipDescription|工具提示中显示在标题下的文本|
|ToolTipImageSource|显示在工具提示中的图像,为止在标题之下,文本描述信息的做ice。图像可以时任意大小|
|ToolTipFooterTitle|显示工具提示页脚标题的文本|
|ToolTipFooterDescription|显示在工具提示页脚部分的文本,位于页脚标题之下|
|ToolTipFooterImageSource|显示在工具提示页脚文本左侧的图像,图像可以时任意大小|

下面列举一个增强的工具提示的例子

1
2
3
4
5
6
7
8
9
10
<RibbonButton
Label="Cut"
LargeImageSource="Images/cut.png"
SmallImageSource="Images/cut.png"
ToolTipDescription="Copies the selected text to the clipboard and removes it"
ToolTipFooterDescription="Press F1 for help"
ToolTipFooterImageSource="Images/help.png"
ToolTipFooterTitle="More Details"
ToolTipImageSource="Images/cut.png"
ToolTipTitle="Cut" />

带有快捷键提示的键盘访问

  键盘用户可访问功能区中的命令。但为了达到这个目的,需要为选项卡、组和命令指定适当的快捷键。

  下面介绍一切配置停当后的工作方式。用户首先按下At 键,然后松开。功能区在应用程序菜单和每个选项卡上显示快捷键提示(单个快捷键字母)。用户可按下字母来选择选项卡或应用程序菜单,此后,功能区显示选项卡(或菜单)中每个启用了快捷键提示的命令的快捷键,如下图所示。最后,用户按下字母来触发相应命令。整个过程需要按键三次,以便键盘用户找到可打开命令的组合键。

1
2
3
4
5
6
7
8
9
10
<RibbonButton
KeyTip="C"
Label="Copy"
LargeImageSource="Images/copy.png"
SmallImageSource="Images/copy.png" />
<RibbonButton
KeyTip="V"
Label="Paste"
LargeImageSource="Images/paste.png"
SmallImageSource="Images/paste.png" />

  为使用 WPF 功能区中的快捷键提示,需要设置 RibbonApplicationMenu、每个 RibbonTab、每个 RibbonMenuItem、RibbonButton 或其他功能区控件的 KeyTip 属性。每个工具提示在其范围内必须是唯一的,这意味着不应为两个选项卡指定相同的工具提示字母,或为一个选项卡中的两个按钮指定相同的工具提示。与 Ofce 应用程序不同,键盘提示始终使用单个字母(不支持两个字母的组合)。如果没有为功能区中的控件指定快捷键提示,就不能使用键盘访问。但只要为 RibbonApplicationMenu指定了工具提示,其中的所有菜单项都可以通过键盘来访问,即使没有快捷键提示也同样如此,原因是用户可使用快捷键提示来打开菜单,并使用方向键在菜单中移动。

改变功能区的大小

  功能区最非凡的功能之一是可以改变自身的尺寸,以通过减少或重新布置每组中的按钮来适应窗口的宽度。

  当使用 WPF创建功能区时,就可以得到基本的尺寸改变功能。尺寸改变功能被构建进RibbonWrapPanel 控件,根据组中控件的数量和组的尺寸使用不同的模板。例如,包含三个RibbonButton 对象的组,如果空间允许的话,将从左向右显示这三个 RibbonButton 对象。如果空间不允许,最右边的控件将被折叠成小图标,然后去掉它们的文本以回收更多空间,最后整组被减小到单个按钮,当单击该按钮时,在下拉列表中显示所有命令。 下图使用具有三个File 组副本的功能区演示了这一过程。第一组完全展开,第二组部分折叠,第三组完全被折叠(有必要指出,为创建这个示例,必须明确地配置功能区不要折看第一组。否则,在完全折看任意组之前,总是尝试部分折叠所有组)。

  可使用几种技术改变功能区组的尺寸。可使用RibbonTab.GroupSizeReductionOrder 属性设置将被首先缩小的组。使用 LabelTitle 值指定每个组。下面是一个示例:

1
<RibbonTab GroupSizeReductionOrder="Clipboard,Files2,Files3" Header="Home">

  当减小窗口的尺寸时,所有组将一点一点地被折叠。然而,Clipboard 组首先被切换到压缩布局,接下来是 Files2组,等等。如果继续收缩窗口,将再次重新布置组,同样首先是 Clipboard组。如果没有设置 RibbonTab.GroupSizeReductionOrder 属性,将首先折叠最右边的组。

  更强大的方法是创建 RibbonGroupSizeDefinition 对象的集合,指示组将如何折叠自身。每个 RibbonGroupSizeDefinition 对象是一个定义单个布局的模板。该模板指定哪些命令应当使用更大的图标,哪些命令应当使用小图标,以及哪些命令应当包含显示文本。下面是一个RibbonGroupSizeDefinition示例,该对象为包含4个控件的组设置布局,使它们使用尽可能大的尺寸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<Ribbon VerticalAlignment="Top">
<Ribbon.Resources>
<RibbonGroupSizeDefinitionBaseCollection x:Key="RibbonLayout">
<!-- All large controls -->
<RibbonGroupSizeDefinition>
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
</RibbonGroupSizeDefinition>

<!-- A large control at both ends, with two small controls in between. -->
<RibbonGroupSizeDefinition>
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
</RibbonGroupSizeDefinition>

<!-- Same as before, but now with no text for the small buttons. -->
<RibbonGroupSizeDefinition>
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
</RibbonGroupSizeDefinition>

<!-- All small buttons. -->
<RibbonGroupSizeDefinition>
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="True" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="True" />
</RibbonGroupSizeDefinition>

<!-- All small buttons, no-text buttons. -->
<RibbonGroupSizeDefinition>
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
<RibbonControlSizeDefinition ImageSize="Small" IsLabelVisible="False" />
</RibbonGroupSizeDefinition>

<!-- Collapse the entire group to a single drop-down button. -->
<RibbonGroupSizeDefinition IsCollapsed="True" />
</RibbonGroupSizeDefinitionBaseCollection>
</Ribbon.Resources>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="Images/imac.png">
<RibbonApplicationMenuItem
Header="New"
ImageSource="Images/new.png"
ToolTip="Create a new document" />
<RibbonApplicationMenuItem
Header="Open"
ImageSource="Images/open.png"
ToolTip="Open a new document" />
<RibbonApplicationMenuItem
Header="Save"
ImageSource="Images/save.png"
ToolTip="Save the document with a new name">
<RibbonApplicationMenuItem Header="Save" ImageSource="Images/save.png" />
<RibbonApplicationMenuItem Header="Save As" ImageSource="Images/save.png" />
</RibbonApplicationMenuItem>
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
<RibbonTab GroupSizeReductionOrder="Clipboard,Tasks,Files" Header="Home">
<RibbonGroup GroupSizeDefinitions="{StaticResource RibbonLayout}" Header="Clipboard">
<RibbonButton
Label="Cut"
LargeImageSource="Images/cut.png"
SmallImageSource="Images/cut.png" />
<RibbonButton
Label="Copy"
LargeImageSource="Images/copy.png"
SmallImageSource="Images/copy.png" />
<RibbonButton
Label="Paste"
LargeImageSource="Images/paste.png"
SmallImageSource="Images/paste.png" />
</RibbonGroup>
<RibbonGroup GroupSizeDefinitions="{StaticResource RibbonLayout}" Header="Files1">
<RibbonButton
KeyTip="N"
Label="New"
LargeImageSource="Images/new.png"
SmallImageSource="Images/new.png" />
<RibbonButton
KeyTip="S"
Label="Save"
LargeImageSource="Images/save.png"
SmallImageSource="Images/save.png" />
<RibbonButton
KeyTip="O"
Label="Open"
LargeImageSource="Images/open.png"
SmallImageSource="Images/open.png" />
</RibbonGroup>
<RibbonGroup GroupSizeDefinitions="{StaticResource RibbonLayout}" Header="Files2">
<RibbonButton
KeyTip="N"
Label="New"
LargeImageSource="Images/new.png"
SmallImageSource="Images/new.png" />
<RibbonButton
KeyTip="S"
Label="Save"
LargeImageSource="Images/save.png"
SmallImageSource="Images/save.png" />
<RibbonButton
KeyTip="O"
Label="Open"
LargeImageSource="Images/open.png"
SmallImageSource="Images/open.png" />
</RibbonGroup>
<RibbonGroup GroupSizeDefinitions="{StaticResource RibbonLayout}" Header="Files3">
<RibbonButton
KeyTip="N"
Label="New"
LargeImageSource="Images/new.png"
SmallImageSource="Images/new.png" />
<RibbonButton
KeyTip="S"
Label="Save"
LargeImageSource="Images/save.png"
SmallImageSource="Images/save.png" />
<RibbonButton
KeyTip="O"
Label="Open"
LargeImageSource="Images/open.png"
SmallImageSource="Images/open.png" />
</RibbonGroup>
</RibbonTab>
</Ribbon>

不仅可以调整功能区的大小,还可以最小化功能区(将其缩小,从而只显示选项卡)。用户可双击任意选项卡标题或右击功能区,然后选择 Minimize the Ribbon 来最小化功能区(也可将其还原)。

快速访问工具栏

  在功能区中将考虑的最后一个要素是快速访问工具栏(或称为 QAT)。它是一个包含常用按钮的窄条,根据用户的选择,QAT可能位于功能区其他要素的上面或下面。

  功能区最初不包含快速访问工具栏。如果想要添加QAT,那么需要通过设置 Ribbon.QuickAccessToolBar 属性来创建。QAT 使用 RibbonQuickAccessToolbar 对象,该对象包含一系列RibbonButton 对象。当为这些对象定义 RibbonCommand 时,只需要提供工具提示文本和小图像,因为文本标签和大图像从不显示。

1
2
3
4
5
6
7
8
9
10
<Ribbon VerticalAlignment="Top">
<Ribbon.QuickAccessToolBar>
<RibbonQuickAccessToolBar>
<RibbonButton Label="Undo" SmallImageSource="Images/undo.png" />
<RibbonButton Label="Undo" SmallImageSource="Images/redo.png" />
<RibbonButton Label="Undo" SmallImageSource="Images/samll-save.png" />
</RibbonQuickAccessToolBar>
</Ribbon.QuickAccessToolBar>
...
</Ribbon>

   QAT 的真正目标是为用户提供自定义区域。应该在OAT中放置较少的项,但允许用户将功能区中最喜欢使用的命令放在 QAT中。可使用两项技巧自动完成这项任务。最重要的是,如果使用命令,那么按钮只能被复制到 OAT 中(换句话说,已经设置了它的 Command 属性,并且使用该属性而非 Click 事件在应用程序中触发适当的操作)。如此一来,用户可通过右击功能区按钮,然后选择 Add to Quick Access Toolbar,将任意命令复制到 QAT 中。同样,用户可右击,然后选择 Remove from Quick Access Toolbar 来从 QAT 中删除按钮。通过将CanAddToQuickAccessToolbarDirectly属性设置为false,可为特定按钮关闭此功能。

  如果功能区充满了项,或将窗口大小调整得非常窄,那么会将 OAT中的一些项移到溢出菜单中。要查看这些项,用户必须单击 OAT右侧的下拉箭头,这会打开一个下拉列表,其中显示了溢出的所有附加命令

  QAT最大的限制在于不能保存当前状态。换句话说,除非您自行构建了相应功能,否则应用程序将无法记住用户已经添加的命令,并会在下次启动应用程序时还原。