WPF入门教程(4):Command与功能区控件
许多数据驱动的应用程序都包含菜单和工具栏或功能区控件,允许用户控制操作。在WPF中,也可以使用功能区控件,因此本文将介绍菜单和功能区控件。
一.菜单控件
在WPF中,菜单可以通过`Menu`和`MenuItem`元素轻松创建。以下是一个示例代码,展示了如何创建一个包含主菜单、次菜单和子菜单项的菜单。
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="文件">
<MenuItem Header="首页"/>
<MenuItem Header="信息">
<MenuItem Header="首页"/>
</MenuItem>
<Separator/>
<MenuItem Header="退出"/>
</MenuItem>
</Menu>
</DockPanel>
</Window>
运行应用程序后,可以看到如下的菜单:
二.功能区控件
菜单控件的替代品是功能区控件。自Microsoft Office 07起,微软引入了新开发的功能区控件。这个新功能的引入使得Office以前版本的许多用户在新的UI中难以找到操作按钮。
WPF功能区控件位于`System.Windows.Controls.Ribbon`命名空间中,需要引用该命名空间。以下是一个示例代码,展示了如何使用功能区控件。
<RibbonWindow x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<Ribbon DockPanel.Dock="Top">
<Ribbon.QuickAccessToolBar>
<RibbonQuickAccessToolBar>
<RibbonButton SmallImageSource="one.png"/>
<RibbonButton SmallImageSource="one.png"/>
</RibbonQuickAccessToolBar>
</Ribbon.QuickAccessToolBar>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="one.png">
<RibbonApplicationMenuItem Header="Hello"/>
<RibbonSeparator/>
<RibbonApplicationMenuItem Header="Exit" Command="Close"/>
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
</Ribbon>
</DockPanel>
</RibbonWindow>
在应用程序菜单中,使用`RibbonTab`元素定义功能区控件的内容。每个`RibbonTab`中包含多个`RibbonGroup`,每个`RibbonGroup`中包含多个`RibbonButton`。
<RibbonTab Header="Home">
<RibbonGroup Header="Clipboard">
<RibbonButton Command="Paste" Label="yi" LargeImageSource="one.png"/>
<RibbonButton Command="Cut" Label="er" LargeImageSource="one.png"/>
<RibbonButton Command="Undo" Label="san" LargeImageSource="one.png"/>
</RibbonGroup>
<RibbonGroup Header="Show">
<RibbonButton Label="yi" LargeImageSource="one.png"/>
<RibbonButton Command="Cut" Label="er" LargeImageSource="one.png"/>
<RibbonButton Command="Undo" Label="san" LargeImageSource="one.png"/>
</RibbonGroup>
</RibbonTab>
第二个`RibbonTab`仅用于演示如何在功能区使用不同类型的控件。
<RibbonTab Header="Ribben Controla">
<RibbonGroup Header="Smaples">
<RibbonButton Label="Button"/>
<RibbonCheckBox Label="Checkbox"/>
<RibbonComboBox>
<Label>one</Label>
<Label>two</Label>
</RibbonComboBox>
<RibbonTextBox>Text Box</RibbonTextBox>
<RibbonSplitButton Label="Split Button">
<RibbonMenuItem Header="One"/>
<RibbonMenuItem Header="Two"/>
</RibbonSplitButton>
<RibbonComboBox Label="Combox2" IsEditable="False">
<RibbonGallery SelectedValuePath="Content" MaxColumnCount="1" SelectedValue="Green">
<RibbonGalleryCategory>
<RibbonGalleryItem Content="red" Foreground="Red"/>
<RibbonGalleryItem Content="Green" Foreground="Green"/>
</RibbonGalleryCategory>
</RibbonGallery>
</RibbonComboBox>
</RibbonGroup>
<RibbonGroup Header="Smaples">
<RibbonButton Label="Button"/>
<RibbonCheckBox Label="Checkbox"/>
<RibbonComboBox>
<Label>one</Label>
<Label>two</Label>
</RibbonComboBox>
<RibbonTextBox>Text Box</RibbonTextBox>
</RibbonGroup>
</RibbonTab>
效果图如下:
三.Commanding
Commanding是WPF中的一个概念,它在动作源和执行动作的目标之间创建松散耦合。这个概念基于Gang Of Four中的命令模式。在WPF中,事件通常是紧密耦合的,而命令则是松散耦合的。
命令模式是一种行为设计模式,它分离了客户和命令的接收者,更加适合进行单元测试。
要执行的动作用于命令对象创建。命令实现`ICommand`接口。WPF使用的命令类是`RoutedCommand`及其派生类`RoutedUiCommand`。`RoutedUiCommand`类定义了一个`ICommand`接口未定义的附加`Text`属性,这个属性可以在用户界面中用作文件信息。`ICommand`定义了`Execute()`和`CanExecute()`方法,它们都在对象上执行。
命令源是调用命令的对象。命令源实现`ICommandSource`接口。这种命令源的例子有派生自`ButtonBase`的按钮类,`HyperLink`、`KeyBinding`、`InputBinding`和`MouseBinging`是派生自`InputBinding`的类。命令源有一个`Command`属性,其中就可以指定实现这个`ICommand`接口的命令对象。
下面举一个例子来说明如何实现命令对象。我们需要先讲解如何定义命令。
四.定义命令
.NET提供了返回预定义命令的类,如`ApplicationCommands`类定义了静态属性`New`、`Open`、`Close`、`Print`、`Cut`、`Copy`、`Paste`等这些属性返回可用于特殊目的的`RoutedUiCommand`对象。还提供了其他类如`NavigationCommands`和`MediaCommands`,它们分别提供了导航和媒体相关的命令。
定义执行应用程序域并不难,为此创建了一个`BooksCommands`类,它通过`ShowBook`与`ShowBookList`属性返回了一个`RoutedUiCommand`。也可以给命令指定一个手势,如`KeyGesture`或`MouseGesture`,这里指定一个`KeyGesture`。用`Alt`修饰`B`键,所以按`Alt+B`组合键可以调用这个命令。我们看以下这个方法的具体实现:
using System.Windows.Input;
namespace WpfApp1
{
public class BooksCommands
{
private static RoutedUICommand s_showbook;
public static ICommand ShowBook =>
s_showbook ?? (s_showbook = new RoutedUICommand("Show Book", nameof(ShowBook), typeof(BooksCommands)));
private static RoutedUICommand s_showBooksList;
public static ICommand ShowBooksList
{
get
{
if (s_showBooksList == null)
{
s_showBooksList = new RoutedUICommand("Show Book", nameof(ShowBook), typeof(BooksCommands));
s_showBooksList.InputGestures.Add(new KeyGesture(Key.B, ModifierKeys.Alt));
}
return s_showBooksList;
}
}
}
}
五.定义命令源
每个实现`ICommandSource`接口的类都可以是命令源,如`Button`和`MenuItem`。在前面创建的功能区控件中,把`Command`属性赋予几个`RibbonButton`元素中,如下列代码所示:
<Ribbon DockPanel.Dock="Top">
<Ribbon.QuickAccessToolBar>
<RibbonQuickAccessToolBar>
<RibbonButton SmallImageSource="one.png" Command="local:BooksCommands.ShowBook"/>
<RibbonButton SmallImageSource="one.png" Command="local:BooksCommands.ShowBooksList"/>
</RibbonQuickAccessToolBar>
</Ribbon.QuickAccessToolBar>
</Ribbon>
一些常见的预命令,例如`ApplicationCommands.Cut`、`Copy`、`Paste`等也赋予`RibbonButton`元素的`Command`属性。对于域命令的命令可以直接在`Command`上写上这些命令。
六.命令绑定
必须添加命令绑定才能把它们连接到处理程序方法上。这里在`Window`元素中定义命令绑定,这样这些就可以操作所有的窗口元素。执行`ApplicationCommands.Close`命令时,会调用`OnClose`;执行`BooksCommands.ShowBook`命令时,会调用`OnShowBooks()`。
<Window.CommandBindings>
<CommandBinding Command="Close" Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
private void CommandBinding_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
Application.Current.Shutdown();
}
...未完待续,我们明天继续讨论数据绑定。敬请期待!