Window类
Window类继承自ContentControl类。这意味着它只能包含单个子元素(通常是一个布局容器,如Grid控件),并且可使用由Background属性设置的画刷绘制背景。还可以使用BorderBrush和BorderThickness属性在窗口周围添加边框,当该边框会被添加到窗口框架之内(在客户区边缘周围)。可通过将WindowStyle属性设置为None,完全移除窗口框架,从而创建一个可完全定制的窗口。
客户区是窗口边界内部的表面,在其中可放置自定义内容。非客户区包括边框和窗口顶部的标题栏。非客户区由操作系统管理。
| 名称 | 说明 |
|---|---|
| AllowsTransparency | 如果设置为 tue,而且如果背景被设置为透明色,Window类就允许其他窗口透过该窗口显示。如果设置为flse(默认值),窗口背后的内容就永远不能显示,并且透明的背景被呈现为黑色背景。当与 WindowStyle属性结合使用,并把 WindowSytle属性设置为 None时,可创建形状不规则的窗口 |
| Icon | 确定希望用于窗口的图标的 IageSource 对象。图标显示在窗口的左上角(假定窗口具有标准的边框样式)、任务栏中(假定 ShowInTaskBar属性为 tmue),以及当用户按下 AIt+Tab 组合键时显示的用于在运行中的应用程序之间进行导航的选择窗口中。因为这些图标具有不同的尺寸,所以.ico 文件应当至少包含一幅 16x16 像索和一幅 32X32 像索的图像。实际上,现代Windows 图标标准还添加了一幅 48x48 像素和一幅 256x256 像素的图像,可以根据其他目的改变它们的尺寸。如果 Icon 属性为空引用,窗口会使用和应用程序相同的图形(在 VisualStudio 中可以通过双击 Solution Explorer 中的Properties 节点,然后选择 Application 选项卡进行设置)。如果将此忽略,WPF将使用标准图标来显示窗口,该图标不是很吸引人 |
| Top和Left | 使用设备无关像素设置窗口左上角到屏幕顶部以及左部边缘之间的距离。当任何细节发生变化时,就会触发 LocationChanged 事件。如果 WindowStartupPosition属性设置为 Manual,可在窗口显示之前通过设置这些属性来设置窗口的位置。当窗口显示之后,可以总是使用这些属性移动窗口的位置,而不管WindowStartupPosition属性被设置为何值 |
| ResizeMode | 可以使用 ResizeMode 枚举值决定用户是否可以改变窗口的尺寸。该设置还会影响是否在标题栏上显示最小化和最大化按钮。使用NoResize值可以完全锁定窗口尺寸,CanMinimize 值只允许最小化窗口,CanResize 值允许任意改变窗口尺寸,CanResize-WithGrip 值在窗口右下角添加图形细节,表示可以改变窗口的尺寸 |
| RestoreBounds | 可以使用 ResizeMode 枚举值决定用户是否可以改变窗口的尺寸。该设置还会影响是否在标题栏上显示最小化和最大化按钮。使用NoResize值可以完全锁定窗口尺寸,CanMinimize 值只允许最小化窗口,CanResize 值允许任意改变窗口尺寸,CanResizeWithGrip 值在窗口右下角添加图形细节,表示可以改变窗口的尺寸 |
| ShowInTasker | 如果设置为 tue,会在任务栏和Alt+Tab 列表中显示窗口。通常,只将应用程序主窗口的这一属性设置为 true |
| SizeToContent | 可使用该属性创建自动放大自身尺寸的窗口。该属性使用SizeToContent枚举值。使用Manual值会禁止窗口自动改变尺寸,使用 Height、Width 或 Width And Height 值允许窗口在不同方向上进行扩展以适应动态内容。当使用SizeToContent属性时,窗口尺寸可以被放大到超出屏幕边界 |
| Title | 显示窗口标题栏(以及任务栏)中的标题 |
| Topmost | 当把该属性设置为true 时,窗口总在应用程序中所有其他窗口的上面显示(除非其他窗口的 TopMost 属性也被设置为 tue)。对于需要“浮动”在其他窗口之上的调色板,这一设置是非常有用的 |
| WindowStartupLocation | 可设置为 WindowStartupLocation 枚举值。如果设置为 Manual 值,就使用 Le 和 Top 属性明确定位窗口:如果设置为 CenterScreen,就在屏幕中央放置窗口;如果设置为 CenterOwncr就在启动该窗口的父窗口的中央显示该窗口。当把非模态窗口的该属性设置为CenterOwncr时,要确保在显示该窗口之前设置新窗口的Ower属性 |
| WindowState | 可设置为 WindowState 枚举值。该属性用于通知当前窗口是否被最大化、最小化或处于正常状态,并允许您进行修改。当该属性改变时会触发StateChanged事件 |
| WindowStyle | 可设置为 WindowStyle 枚举值,该属性决定了窗口的边框。可选择SingleBorderWindow(默认值)、None(在没有标题栏的区域周围有一条凸起的细边框),另外两个选项已基本上废弃不用(ThreeDBorderWindow和ToolWindow)。如果准备在 Windows8上运行应用程序情况尤其如此 |
此外,Window 类还提供了LocationChanged 和 WindowStateChanged 事件,当窗口的位置和WindowState 属性发生改变时会相应地触发这两个事件。
显示窗口
为显示窗口,需要创建 Window 类的实例并使用 Show()或 ShowDialog( )方法。
ShowDialog()方法显示模态窗口。模态窗口通过锁住所有鼠标和键盘输入来阻止用户访问父窗口,直到模态窗口被关闭。此外,直到模态窗口被关闭后,ShowDialog()方法才返回。所以,在 ShowDialog()方法调用之后放置的任何代码都会被阻塞(然而,这并不意味着其他代码无法运行。 例如,如果有正在运行的计时器,那么计时器的事件处理程序仍将运行)。在代码中,通常的模式是显示一个模态窗口,等待直到它被关闭,然后使用它的数据。
下面是使用
ShowDialog()方法的一个示例
1 | TaskWindow winTask = new TaskWindow(); |
**Show()方法显示非模态窗口,非模态窗口不会阻止用户访问其他任何窗口。而且当窗口显示之后,Show()方法会立即返回,所以后续代码语句会立即执行。**可创建并显示几个非模态窗口,而且用户能够同时和它们进行交互。当使用非模态窗口时,有时需要同步代码,以确保在一个窗口中的改变能更新另一个窗口中的信息,从而防止用户使用无效的信息。
下面是使用
Show()方法的一个示例
1 | MainWIndow winMain = new MainWindow(); |
关闭窗口非常简单,只需要使用Close()方法。此外,可使用Hide()方法或将 Visibility 属性设置为 Hidden 来隐藏窗口。不管使用哪种方式隐藏窗口,对于代码来说窗口仍然是打开的和可见的。通常,只隐藏非模态窗口是有意义的。因为如果隐藏模态窗口,代码执行会被阻塞(直到窗口被关闭为止),然而用户不能关闭不可见的窗口。
定位窗口
通常不需要在屏幕上准确定位窗口。只将窗口状态设置为CenterOwner,并忽略其他所有细节。另一方面,在较少情况下,会将窗口状态设置为 Manual,并使用Left 和 Right属性准确设置窗口的位置。
可限制窗口的尺寸以使其支持最小的显示器,但是这会让高端用户感到沮丧(它们为了在屏幕上一次显示更多信息,已经专门购买了更好的显示器)。对于这种情况,通常希望在运行时决定窗口的最佳位置。为此,需要使用 System.Windows.SystemParameters 类来检索有关屏幕实际大小的基本信息。
**SystemParameters 类包含大量静态属性,可返回各种有关系统设置的信息。例如,可使用SystemParameters 类决定用户是否启用了热跟踪(hot tracking)选项、拖动整个窗口选项以及其他选项。对于窗口,SystemParameters 类特别有用,因为它提供的两个属性可给出当前屏幕的大小: FullPrimaryScreenHeight 和 FullPrimaryScreenWidth。**这两个属性都非常简单,下面是一些(在运行时将窗口定位在屏幕中央)演示代码:
1 | double screeHeight = SystemParameters.FullPrimaryScreenHeight; |
尽管使用这些代码和将窗口状态设置为CenterScreen的效果相同,但使用代码具有实现不同定位逻辑的灵活性,并且可在适当的时间运行这些定位逻辑。
更好的选择是使用 SystemParameters.WorkArea矩形,使窗口位于可用屏幕区域的中央。工作区域不包括停靠任务栏(以及其他停靠到桌面的工具栏)的区域。
1 | double workHeight = SystemParameters.WorkArea.Height; |
这两个窗口定位示例都存在一个小的缺陷。当为已经可见的窗口设置 Top 属性时,窗口会被立即移动和刷新。当使用后面的代码行设置Le属性时,会发生同样的过程。因此,视觉敏锐的用户会看到两次窗口移动。但是,Window 类没有提供方法来同时设置这两个位置属性。唯一的解决方法是在窗口创建之后,但尚未通过调用 Show()或 ShowDialog( )方法显示之前定位窗口。
保存和还原窗口位置
经常需要让窗口记住它上一次的位置。这一信息可存储到特定于用户的配置文件或Windows注册表中。
如果希望在特定于用户的配置文件中存储重要窗口的位置,可首先双击 Solution Explorer 中的 Properties 节点,然后选择 Settings 选项卡。接着使用 System.Windows.Rect 数据类型(但是发现没有Rect类型,因此这里采用string,需要自行序列化)添加用户范围的设置,如下图所示。
1 | var win = new Window(); |
需要注意的是,上面的代码使用了RestoreBounds属性,即使窗口当前被最大化或最小化该属性也能提供正确的范围(最后的非最大化和非最小化尺寸)。当需要时,检索此信息也同样很简单:
1 | try |
该方法唯一的限制是需要为每个准备保存的窗口创建单独的属性。如果需要为许多不同的窗口保存位置,可能希望设计一个更灵活的系统。例如,下面的辅助类为传递给它的任何窗口保存位置,并使用包含窗口名称的注册表键(如果希望为几个同名窗口保存设置,可以使用附加的标识信息)。
1 | public class WindowPositionHelper |
为在窗口中使用该类,当窗口关闭时调用SaveSize()方法,并当首次打开窗口时调用SetSize( )方法。对于每种情况,都需要传递窗口引用,使其指向希望辅助类检査的窗口。需要注意的是在这个示例中,每个窗口的Name 属性的值都不能相同。
窗口交互
Application类提供了用于访问其他窗口的两个工具:MainWindow和Windows属性。如果希望按自己的方式跟踪窗口,例如保持跟踪某个特定窗口类的实例(该类可能代表文档),可在Application类中添加自己的静态属性。
当然,获取其他窗口的引用只是工作的一部分,还需要决定如何进行通信。作为通用规则应当尽可能减少窗口之间交互的需要,因为这会无谓地增加代码的复杂性。如果确实需要根据一个窗口中的动作修改另一个窗口中的控件,可在目标窗口中创建专用方法。这样可确保依赖性被很好地标识,并添加额外的间接层,这样更容易适应窗口接口的变化。
如果两个窗口之间具有复杂的交互,并且单独地被开发或部署,或者很可能会改变,这时可以考虑得更长远一些,可以通过创建具有公共方法的接口来规范化它们之间的交互,并在自定义的窗口类中实现该接口。
下图显示了这一模式的两个示例,第一个示例中,一个窗口通过触发第二个窗口来刷新它的数据,以便响应按钮的单击事件。该窗口没有试图直接修改第二个窗口的用户界面,而是依赖于自定义的名为DoUpdate()的中间方法。
显示了需要更新多个窗口的情况。对于这种情况,动作窗口依靠一个更高层的应用程序方法,该方法调用所需的窗口更新方法(可能通过迭代窗口集合来实现)。这种方法的效果更好,因为可以在更高层次上工作。上图显示的方法中,动作窗口不需要了解有关接收窗口中控件的特定内容。下图显示的方法中更进一步——动作窗口根本不需要了解接收窗口的任何内容。
当在窗口之间进行交互时,Window.Activate()方法通常可以带来方便。该方法可将所期望的窗口转换为活动窗口。还可以用 WindowIsActive 属性测试某个窗口当前是否是活动窗口,以及是否是唯一的活动窗口。
可以进一步降低该例中窗口之间的耦合程度。不是使用Application类触发各窗口中的方法而是仅仅触发一个事件,并允许窗口选择如何响应该事件。
窗口所有权
.NET 允许一个窗口“拥有”其他窗口。对于浮动的工具框和命令窗口而言,被拥有的窗口是很有用的。被拥有的窗口的典型示例是 Microsoft Word 中的查找和替换窗口。当所有者窗口最小化时,也会自动最小化被拥有的窗口。当被拥有的窗口与拥有者窗口相互重叠时,被拥有的窗口总显示在上面。
为支持窗口之间的拥有关系,Window 类增加了两个属性。0wer属性是指向窗口的引用,引用的窗口拥有当前窗口(如果存在的话)。OwnedWindows 属性是当前窗口拥有的所有窗口(如果包含有窗口的话)的集合。
1 | // Create a new window |
被拥有的窗口始终以非模态方式显示。为移除被拥有的窗口,需要将其Owner 属性设置为空引用。
WPF 没有提供用于构建多文档用户界面(MDI)应用程序的系统。如果希望更复杂的窗口管理,那么需要自己构建(或购买第三方组件)。
被拥有的窗口可拥有其他窗口,后者又可以拥有另外的窗口,等等(尽管这种设计的实际用途值得怀疑)。唯一的限制是窗口不能拥有自身,并且两个窗口不能相互拥有。
对话框模型
&emsp通常,当显示模态窗口时,是为了给用户提供一些选择。显示窗口的代码等待选择结果,通常,当显示模态窗口时,然后根据选择执行相应的操作。这种设计称为对话框模型(dialog model)。以模态方式显示的窗口就是对话框。
通过在对话框中创建一些公共属性,可以很容易地使用这种设计模式。当用户在对话框中进行选择时,将会设置这些属性,然后关闭对话框。此后显示对话框的代码可检查相应的属性并根据其值决定下一步要进行的操作(需要注意,即使对话框已经被关闭,对话框对象及其所有控件信息也仍然存在,直到引用变量超出了其作用域为止)。
幸运的是,这种结构的某些内容已经被固化到 Window类中。每个窗口都包含预先准备好的 DialogResult 属性,该属性可以设置为tue、false 或 nul 值。通常,tue 表示用户选择继续进行操作(例如,单击 OK 按钮),而false 表示用户取消了操作。
最方便的是,一旦设置 DialogResult属性,就会为调用代码返回该属性值作为 ShowDialog()方法的返回值。这意味着可使用下面的简洁代码来创建、显示对话框并使用其结果:
1 | DialogWindow dialog = new DialogWindow(); |
使用 DialogResult 属性没有妨碍为自己的窗口添加自定义属性。例如,使用 DialogResult 属性通知调用代码某个动作是否被接受或取消,并通过自定义属性提供其他重要细节,这是非常合理的。如果调用代码发现 DialogResult属性为 tue,就可以检查其他属性以获取它所需要的信息。
还可以使用其他更简便的方法。不是当用户单击了按钮后手动设置 DialogResut 属性,而是将按钮指定为接受按钮(通过将IsDefault属性设置为true)。当单击按钮时会自动地将对话框的 DialogResult 属性设置为 true。类似地,可将按钮指定为取消按钮(通过将IsCancel 属性设置为 true),对于这种情况,单击按钮会将 DialogResult属性设置为Cancel
通用对话框
Windows操作系统提供了许多内置对话框,可通过Window API访问这些对话框。WPF为其中的即可对话框提供了封装程序。
由于多种原因,WPF 没有为所有 Windows API 提供封装程序。WPF 的目标之一是与WindowsAP相互独立,从而可在其他环境(如浏览器)中使用或移植到其他平台。此外,许多内置对话框是非常古老的,对于现代应用程序它们不应当是第一选择。
MessageBox
最常见的对话框是 System.Windows.MessageBox类,该类提供了静态的 Show()方法。可使用该类显示标准的 Windows 消息框。下面是最常用的重载方法:
1 | MessageBox.Show("You must enter a name.","Name Entry Error",MessageBoxButton.OK,MessageBoxImage.Exclamation); |
PrintDialog
专门用于打印的类
OpenFileDialog和SaveFileDialog
OpenFileDialog类用于弹窗选择某个文件,SaveFileDialog用于弹窗选择保存文件.
1 | OpenFileDialog myDialog = new OpenFileDialog(); |
WPF 没有提供颜色拾取器、字体拾取器以及文件夹浏览器(但可使用.NET 2.0中的 System.Windows.Forms 类获取这些要素)。
非矩形窗口
对于时髦的客户应用程序,经常会使用形状不规则的窗口,如图片编辑程序、视频制作程序以及 MP3 播放器。在 WPF 中创建使用基本形状的窗口是非常容易的。然而,创建精致的、具有专业外观的窗口需要完成更多工作–并且,很可能需要由优秀的图形设计人员创建轮廓并设计背景插图。
简单形状窗口
创建简单形状窗口需要使用以下几个步骤:
- 将
Window.AllowsTransparency属性设置为true - 将
Window.WindowStyle属性设置为Node,从而隐藏窗口的非客户区(蓝色边框)。如果不这样做,当试图显示窗口时会抛出InvalidOperationException异常 - 将窗口背景设置为透明(可使用Transparent颜色,该颜色的alpha值为0),或将背景设置为具有透明区域的图像(透明区域时使用alpha值为0的颜色绘制的区域)
这三个步骤可有效地移除窗口的标准外观(对于WPF专业人员来说,这些标准外观就是窗口的装饰元素)。为得到简单形状窗口效果,现在需要提供一些不透明的具有所需形状的内容。有多种选择:
- 使用支持透明格式的文件提供背景插图。例如,可使用PNG文件提供窗口的背景。这是比较简明直观的方法,并且如果正在和未掌握XAML知识的设计人员合作,这种方法是比较合适的。然而,因为在具有更高系统DPI的窗口中呈现时需要使用更多的像素,背景图形可能变得模糊。当允许用户改变窗口尺寸时,也存在这一问题。
- 使用WPF中的形状绘制功能创建具有矢量内容的背景。不管是改变窗口尺寸还是改变系统DPI设置,该方法可以确保不损失质量。然而,可能希望使用支持XAML的设计工具,例如Expression Blend
- 使用更简单的具有所需形状的WPF元素。例如,可使用Border元素创建出具有圆角边缘的美观窗口。通过这一方法不需要额外的设计工作,就可以实现具有现代Office风格的窗口外观
1 | <Window |
具有形状内容的透明窗口
在大多数情况下,WPF窗口不实用固定图形创建任意形状窗口。相反,会使用完全透明的背景,然后在该背景上放置形状内容。该方法的优点时更加模块化。可使用许多独立组件组装窗口,所有这些组件都是基本的WPF元素。而且更重要的是,该方法可以充分利用其他WPF功能构建真正的动态用户界面。例如,可在窗口中组装能改变尺寸的形状内容或使用动画创建一直运行的效果。如果使用单一的静态文件提供图形,这是很难实现的。
1 | <Window |
目前,Path 元素的尺寸是固定的(窗口的尺寸也是固定的),但可以将之放到 Viewbox 容器中以改变尺寸。通过增加关闭按钮的逼真度——在红色表面上绘制矢量的 X 图标,还可以进一步改进该例。尽管可使用单独的 Path 元素代表按钮并处理按钮的鼠标事件,但是使用控件模板来改变标准 Button 控件会更好一些。然后可让 Path 元素绘制白定义按钮的 X 图标部分。
移动形状窗口
形状窗体所受的限制之一是没有属于非客户区的标题栏部分,标题栏允许用户很容易地在桌面上拖动窗口。幸运的是,在 WPF 中,可随时通过调用 Window,DragMove()方法启动窗口拖动模式。
所以,为允许用户拖动在上一个示例中看到的任意形状窗体,只需要为窗口(或窗口上的-个元素,该元素将扮演和标题栏相同的角色)处理 MouseLefButtonDown 事件:
1 | <TextBlock Text="Title Bar" Margin="1" Padding="5" MouseLeftButtonDown="titleBar_MouseLeftButtonDown"/> |
在事件处理程序中只需一行代码:
1 | private void titleBar_MouseLeftButtonDown(object sender,MouseButtonEventArgs e) |
现在窗口就会跟随鼠标在桌面上移动了,直到用户释放鼠标为止
改变形状窗口的尺寸
改变形状窗口的尺寸不是很容易。如果窗口的形状大体上是矩形,最简单的方法是通过将Window,ResizeMode 属性设置为 CanResizeWithGrip,这时会在窗口的右下角添加用于改变窗口尺寸的图形手柄(grip)。
如果手柄不能放到窗口的正确位置上,或者希望允许用户通过拖动窗口边缘来改变窗口的尺寸,就需要进行更多的工作。可使用两种基本方法。可使用NET的平台调用特性(Pnvoke)发送改变窗口尺寸的 Win32 消息。或者,当用户拖动一条侧边时,简单地跟踪鼠标位置,并通过设置窗口的 Width 属性来手动改变窗口的尺寸。下面的示例使用后一种技术。
在使用每种技术之前,需要有一种方式来探测用户何时会将鼠标移到窗口边缘上。这时,鼠标指针应当变为可以改变尺寸的光标。在 WPF中完成该任务的最简单方法是在每个窗口的边上放置一个元素。该元素不需要具有任何可视化外观–实际上,它可以是完全透明的并且可以让窗口透过该元素显示。它唯一的目的是截获鼠标事件。
1 | <Rectangle |
1 | public partial class MainWindow : Window |
窗口的自定义控件模板
使用到目前为止看到的代码,可很容易地构建具有自定义形状的窗口。然而,如果希望为整个应用程序使用新的窗口标准,就必须以手动方式重新设置具有相同形状边框、题头区域和关闭按钮等内容的所有窗口。对于这种情况,更好的方法是将标记改编成可用于任意窗口的控件模板。
第一步是查阅 Window 类的默认控件模板。对于大部分内容,这个模板是很简单的,但是它包含了一个您可能不希望的细节:AdormerDecorator 元素。这个元素在窗口的其他客户内容之上创建了一个特定的绘图区域,称为装饰层(adorner layer)。WPF 控件可使用装饰层绘制应当在元素上重叠显示的内容,包括一些用于显示焦点、标志验证错误以及指导拖放操作的小图形指示内容。当构建自定义窗口时,需要确保提供装饰层,从而可以让使用装饰层的控件能够继续使用。
为提供装饰层,可使用下面的标记确定窗口控件模板应当采用的基本结构。下面是一个标准化示例:
1 | <Window |
Windows7任务栏变成
Windows7和Windows 8(在桌面模式下)使用重新设计的任务栏,它们添加了一些 Windows Vista中不具备的几个增强功能。WPF 可以很好地支持这些功能,不会迫使开发人员添加非托管的API调用或依赖于独立程序集。
WPF 不但为跳转列表(当右击任务栏按钮时显示的列表)提供基本支持,还允许改变应用程序使用的任务栏预览图像和任务栏图标。接下来几节将看到这些特性。
可在定向到 Windows Vista 的应用程序中安全地使用 Windows任务栏特性。用于与增强的Windows7和 Windows8任务栏交互的任何标记或代码在 Windows Vista 上都会安全地被忽略,如果将.NET4作为目标,而且您正在构建可在 WindowsXP 计算机上运行的应用程序,情况同样如此。WPF会简单地忽略不适用的任务栏特性。
使用跳转列表
跳转列表(jump list)是当右击任务栏按钮时弹出的便捷小菜单。为当前正在运行的应用程序以及当前没有运行但具有锁定到任务栏上的按钮的应用程序显示跳转列表。通常,跳转列表为打开属于恰当应用程序的文档提供了一种快速方法 —— 例如,打开 Word 的最近文档或在Windows Media Player中频繁播放的歌曲。然而,有些程序使用更富创意的方法执行特定于应用程序的任务。
最近文件支持
Windows7和 Windows8中的任务栏为所有基于文档的应用程序添加了跳转列表,前提是将应用程序注册为处理特定的文件类型。例如,在第7章介绍了如何构建单实例应用程序(称为SinglelnstanceApplication),该应用程序注册为处理.textDoc 文件。当运行该程序并右击其任务栏按钮时,将看到最近打开的文档列表,下图所示。
如果右击跳转列表中的某个最近访问的文档,Windows会加载应用程序的另一个实例,并作为命令行参数传递完整的文档路径。当然,如果这不是所希望的行为,可通过代码进行修改。例如,对于第7章中的单实例应用程序。如果从其跳转列表打开一个文档,新实例会不加通告地将文档路径传递到当前正在运行的应用程序,然后关闭自身。最终结果是同一个应用程序处理所有文档,无论从应用程序内部还是从跳转列表打开均如此
正如文档所记载的,为得到最近文档支持,必须将应用程序注册为处理相应的文件类型。可以使用两种简便方法完成该工作。第一种方法,可以使用代码为 Windows注册表添加相关细节,如第7章所述。第二种方法,可通过 Windows资源管理器手动完成该工作。下面是完成该工作的简要步骤:
- 右击恰当的文件(例如,具有*.testDoc*扩展名的文件)
- 选择Open With|Choose Default Program菜单项以显示Open With对话框
- 单击Browser按钮,查找WPF应用程序的.exe文件并选择该文件
- 可酌情禁用Always use selected program to open this kind of file选项。您的应用程序不需要称为具有跳转列表支持的默认应用程序。
- 单击OK按钮
当注册文件类型时,需要牢记以下几条指导原则:
- 当创建文件类型注册时,为 Windows提供可执行程序的准确路径。所以应在将应用程序放置到某个合理的位置之后再进行注册,否则每次移动了应用程序文件之后都需要重新注册。
- 不用担心会取代通用文件类型。只要不使应用程序成为该文件类型的默认处理程序,就不会改变 Windows 的工作方式。例如,将您的应用程序注册为处理.txt 文件是完全可以接受的。这样,当用户使用指定的应用程序打开一个.xt文件时,该文件显示在应用程序的最近文档列表中。类似地,如果用户从指定的应用程序的跳转列表中选择一个文档Windows 会加载您的应用程序。然而,如果在 Windows 资源管理器中双击.txt 文件Windows 仍会为.xt 文件启动默认的应用程序(通常是记事本)。
- 为在 Visual Studio 中测试跳转列表,必须关闭运行 VisualStudio 承载进程。如果正在运行Visual Studio,Windows 将为承载进程(YourApp.vshost.exe)检查文件类型注册,而不是为您的应用程序(YourApp.exe)检査文件类型注册。为避免该问题,直接从 Windows 资源管理器或选择 Debug|Start Without Debugging 菜单项来运行编译过的应用程序。对于任意一种方法,当测试跳转列表时都不会得到调试支持。
Windows 不但为您的应用程序方便地提供最近文档列表,而且还支持锁定(pinning),锁定允许用户将他们最重要的文档附加到跳转列表中并永远保留它们。与所有其他应用程序的跳转列表相同,用户可通过单击小的缩略图标来锁定文档。Windows然后将选择的文件移到单独的列表类别中,这就是所谓的锁定。类似地,用户可通过右击并选择Remove 来将一个项从最近文档列表中删除。
自定义跳转列表
您到目前为止看到的跳转列表支持被构建进了Windows,并且不需要任何 WPF 逻辑。然而,WPF通过允许控制跳转列表并且允许填充自定义项,添加了对跳转列表的支持。为此,只需要在 App.xaml 文件中添加一些定义<JumpList.List>节点的标记,如下所示:
1 | <Application |
当采用这种方式定义定制的跳转列表时,Windows停止显示最近文档列表。为使Windows继续显示最近文档列表,需要使用 JumpList.ShowRecentCategory属性明确地进行选择。还可添加 ShowFrequentCategory 属性,以便为那些注册应用程序处理的最频繁打开的文档显示跳转列表。
当创建 JumpPath 对象时,可提供两个细节。CustomCategory 属性设置在跳转列表中的项之前显示的标题(如果添加了几个具有相同类别名的项,它们将被分组到一起)。如果没有提供类别,Windows 使用名为 Tasks 的类别。Path 属性是指向文档的文件路径。路径必须是完全限定的文件名,文件必须存在,并且必须是应用程序注册处理的文件类型。如果违反这些规则,就不会在跳转列表中显示指定的项。
| 名称 | 说明 |
|---|---|
| Title | 在跳转列表中显示的文本 |
| Description | 当将鼠标悬停在项上时显示的工具提示文本 |
| ApplicationPath | 应用程序的可执行文件。与JumpList中的文档路径一样,ApplicationPath属性需要完全限定的路径 |
| IconResourcePath | 指向具有缩略图图标的文件,Windows将在跳转列表中该项的旁边显示该图标。说来也怪,Windows不是选择默认图标,也不是从应用程序的可执行文件中提取图标。如果希望看到有效的图标,就必须设置IconResourcePath属性 |
| IconResourceIndex | 如果 lconResourccPat 指定的应用程序或图标资源有多个图标,那么还需要使用lconResourceImdexed 来选择希望使用的图标 |
| WorkingDirectory | 应用程序将开始启动的工作目录,通常是包含应用程序文档的文件夹 |
| ApplicationPath | 希望传递到应用程序的命令行参数,如打开的文件 |
使用代码创建跳转列表项
尽管在 App.xaml文件中使用标记填充跳转列表很容易,但这种方法存在严重缺陷。正如您已经看到的,JumpPath 和JumpTask项都需要完全限定的文件路径。然而,该信息通常取决于应用程序的部署方式,从而不应当为其使用硬编码。为此,通常使用代码创建或修改应用程序跳转列表。
**为使用代码配置跳转列表,使用System.Windows.Shell名称空间中的JumpList、JumpPath以及 JumpTask 类。**下面的示例通过创建新的 JumpPath 对象演示了这种技术。使用该项,用户可打开记事本,查看存储在当前应用程序的文件夹中的readme.txt 文件(不考虑其安装位置)。
1 | private void App_OnStartup(object sender, StartupEventArgs e) |
从跳转列表启动应用程序任务
为使用跳转列表触发任务,需要使用在第7章中使用的单实例技术的变体。这不是WPF跳转列表类中的疏忽–跳转列表就是这么设计的。下面是基本策略:
- 当引发 Application.Startup 事件时,创建指向应用程序的JumpTask 对象。不是使用文件名,而是将 Arguments 属性设置为应用程序识别的特殊编码。例如,如果希望该任务向应用程序传递一条“startorder”指令,可将该属性设置为@#StartOrder。
- 使用第 7 章中的单实例代码。当启动第二个实例时,向第一个实例传递命令行参数,并关闭新的应用程序。
- 当第一个实例(在 OnStartupNextInstance( )方法中)接收到命令行参数时,执行适当的
任务。 - 当引发 Application.Exit 事件时,务必从跳转列表中删除任务,除非当应用程序第一次启动时,任务命令能够同样很好地工作。
改变任务栏图标和预览
Windows7和 Windows8中的任务栏增加了几个更精致的改进之处,包括可选的进度显示和缩略预览窗口。幸运的是,WPF 使得使用所有这些功能都很容易。
为访问这些功能中的任意一个,需要使用 TaskbarltemInfo类,该类与前面分析的跳转列表类一样,也位于 System.Windows.Shell 名称空间中。每个窗口都有一个关联的 TaskbarltemInfo对象,并且可通过为窗口添加下面的标记来使用XAML创建该对象:
1 | <Window |
就其本身而言,这一步没有改变窗口或应用程序中的任何内容,但是现在已经准备好使用后面将要演示的特性了。
缩略图裁剪
与 Windows 为所有应用程序自动支持跳转列表很相似,WPF还提供了当用户将鼠标悬停在任务栏按钮上时会显示的缩略图预览窗口。通常,缩略图预览窗口显示窗口客户区(除窗口边框之外的所有内容)的缩小版。然而在某些情况下,可能希望只显示窗口的一部分。这种方式的优点是可以只关注窗口中的相关部分。在特大窗口中,这可能是合理的,否则内容会太小而无法阅读。
可使用 TaskbarltemInfo.ThumbnailClipMargin属性使用该功能。该属性指定 Thickness 对象,设置希望在缩略图中显示的内容和窗口边缘之间的空间。下图显示的示例演示了其工作原理。每次用户单击应用程序中的一个按钮时,就将剪裁区域转换为仅包含被单击按钮的区域。
为创建该效果,代码必须考虑几个细节:按钮的坐标、尺寸以及窗口内容区域的尺寸(窗口内容区域的尺寸也就是顶级的名为 LayoutRoot的 Grid 面板的尺寸,这很有帮助,该 Grid 面板位于窗口的内部并且包含其所有标记)。一旦具备这些数值,为了仅在正确的区域预览所需的内容,只需要执行几个简单计算:
1 | private void cmdShrinkPreview_Click(object sender, RoutedEventArgs e) |
缩略图按钮
有些应用程序将预览窗口用于完全不同的目的,它们将按钮放到预览下面的小工具栏区域中。Windows媒体播放器就是一例。如果将鼠标悬停在任务栏图标上,将得到包含播放、暂停前进以及后退按钮的预览,从而提供了一种用于控制播放而不需要切换到应用程序本身的便捷方式。
WPF 支持缩略图按钮——实际上,WPF 使用它们更容易。只需要为 TaskbarltemInfo.ThumbButtonInfos 集合添加一个或多个 ThumbButonInfo 对象即可。每个 ThumbButtonInfo 对象需要一幅图像,使用 ImageSource 属性提供该图像。还可以用 Description 属性添加工具提示文本。然后可通过处理其 Click 事件将按钮关联到应用程序中的某个方法。
1 | <Window.TaskbarItemInfo> |
当应用程序执行不同任务并且进入不同的状态时,有些任务栏按钮可能不适合。幸运的是可使用少数几个有用的属性对任务栏按钮进行管理,下表列出了这些属性:
| 名称 | 说明 |
|---|---|
| ImageSource | 设置希望为按钮显示的图像,该图像必须作为资源嵌入到应用程序中。理想情况是,使用具有透明背景的.png文件 |
| Description | 设置当鼠标悬停在按钮上时显示的工具提示文本 |
| Command CommandParameter CommandTarget | 指定按钮应当触发的命令。可使用这些属性,而不使用Click事件 |
| Visibility | 用于隐藏或显示按钮 |
| IsEnabled | 用于禁用按钮,使按钮可见当不能单击 |
| IsInteractive | 用于禁用按钮并且不会使外观变暗。如果希望按钮就像时某种状态指示器,这很有用 |
| IsBackgroundVisible | 用于为按钮禁用悬停反馈。如果为true(默认值),当将鼠标移动按钮上时,Windows会突出显示按钮并显示其周围的边框,如果为false,就不显示 |
| DismissWhenClicked | 用于创建只使用一次的按钮。只要单击按钮,Windows就会将其成任务栏中移除(为了加强控制,可使用代码随时添加或移除按钮,但使用Visibility属性显示和隐藏按钮通常更容易) |
进度通知
如果在 Windows 资源管理器中复制过大文件,那么应当看到过如何使用进度通知将任务栏按钮的背景着色为绿色。当进行复制时,绿色背景从左向右地填充按钮区域,就像是进度条,直到操作完成为止。
您可能没有意识到,该特性不仅仅特定于 Windows资源管理器。反而,该特征被构建进了Windows7中,并且所有WPF应用程序都可以使用该特性。需要做的全部工作就是使用TaskbarItemInfo类的两个属性:ProgressValue和ProgressState.
ProgressState 属性刚开始时被设置为 None,这意味着不显示进度指示器。然而,如果将其设置为 TaskbarltemProgressState.Normal,就会得到 Windows 资源管理器使用的绿色进度背景ProgressValue 属性决定了绿色背景的尺寸,从 0(没有绿色背景)到 1(完整的绿色背景)。例如,将ProgressValue属性设置为0.5,就会用绿色填充任务栏按钮背景的一半。
TaskbarltemProgressState 枚举提供了几个可能的值,而不仅是 None 和 Normal。可使用 Pause显示黄色背景而不是绿色,使用Error显示红色背景,以及使用Indeterminate显示忽略ProgressValue 属性的持续的、脉冲进度背景。当不知道完成当前任务需要多长时间时(例如,当调用 Web 服务时),最后一个选项是合适的。
重叠图标
Windows提供的最后一个任务栏特性是任务栏重叠–在任务图标上添加小图像的能力。例如,Messenger聊天程序使用不同的重叠图标指示不同状态。
为使用重叠图标,只需要一个很小的具有透明背景的.png或.ico 文件。不强制使用特定的像素尺寸,但显然希望图像比任务栏按钮图片更小一点。假定已为项目添加了该图像,可通过简单地设置 TaskbarItemInfo.Overlay属性进行显示。通常,将使用已在标记中定义的图像资源对其进行设置,如下所示:
1 | taskBarItem.Overlay = (ImageSource)this.FindResource("WorkingImage"); |
作为一种选择,可使用熟悉的pack URI语法指向嵌入的文件,如下所示:
1 | taskBarItem.Overlay = new BitmapImage(new Uri("pack://applicationL,,,/working.png")); |
将 Overlay 属性设置为空引用会彻底移除重叠图标,下图显示的pause.png图像被用作通用WPF应用程序图标上面的重叠图标,这指示当前暂停了应用程序的工作。