使用 Windows PowerShell 构建 WPF 应用程序的奥秘

Windows PowerShell 将任务自动化提升到一个全新的高度。 它非但没有抛弃旧技术,还将这些技术更加发扬光大。 采用 Windows PowerShell(以下简称 PowerShell)并不表示必须重新构建现有的应用程序才能继续使用它们。 实际上,您可以使用 PowerShell 无缝集成并扩展现有的应用程序。

PowerShell 是一种自动化技术,以命令行界面 (CLI)、脚本语言和 API 的形式呈现。

在本文中,我将演练一些重要的 PowerShell 方法,并使用 Windows Presentation Foundation (WPF) GUI 构建一个现值计算器(bit.ly/7oEij1,参见图 1)。

图 1 PowerShell 现值计算器

我将介绍几个重要的 PowerShell 元素:WPF PowerShell Kit (WPK);对象管道;函数;PSObject 及属性;与 Microsoft .NET Framework 的无缝集成;模块等。

PowerShell 构建于 .NET Framework 基础之上,您可以像使用其他 .NET 语言一样顺畅访问该框架。 此外,您还可以访问 Windows 操作系统的其余部分及其组件(如服务、进程和 Windows Management Instrumentation (WMI)),并可以访问远程服务器(这些服务器也具有 PowerShell 版本 2 并启用了 Windows 远程管理)。

所有这一切都通过 PowerShell 所公开的新脚本语言来实现。 您只需使用记事本和几个 PowerShell cmdlet(发音为“command-let”,cmdlet 是在 PowerShell 环境中使用的轻型命令)。 好消息是,这些都是现成可用的: cmdlet 内置于 PowerShell 之中,而记事本随 Windows 附带。 三个重要的 cmdlet:Get-Help 用于显示有关 PowerShell 命令和概念的信息;Get-Command 用于获取有关 cmdlet 和其他 PowerShell 命令元素的基本信息;Get-Member 用于获取对象的“成员”(属性和方法)。

这三个 cmdlet 可以执行所有发现功能,帮助您在这一新的任务自动化平台中进行导航。

让我们进入正题

问题:若要创建一个带有“Hello World”标签的 WPF 应用程序,需要多少行代码?

答案:如果使用 PowerShell 和 WPK,则只需两行代码,如图 2 所示。

图 2 只有两行代码的 PowerShell WPF 应用程序

这是使用两行 PowerShell 编写的一个完整 WPF 应用程序。 第 1 行 (Import-Module WPK) 导入 WPK 包,其中包含一组包装 WPF 的 PowerShell cmdlet。 有趣的是,此代码的运行不需要 Visual Studio、XAML 或 C#。 不过,您需要安装 WPK(请参见下一节)。

Windows 7 和 Windows Server 2008 R2 中提供了现成的 PowerShell 版本 2(对于早期 Windows 系统,可下载使用)。 在发布客户端和服务器操作系统的同时,也发布了 PowerShell 包(作为单独的下载),其中包含 WPK。 它的实现效仿了流行的 Unix 脚本工具 Tcl/Tk。

我先从一组简单的 PowerShell 变量开始,逐步构建出一个交互式 WPF 应用程序。 我将使用 PowerShell 集成脚本环境 (ISE)。

想要继续下去么?

如果您有 Windows 7,那么已基本准备就绪(请记住,PowerShell 是内置的)。

如果您运行的不是 Windows 7,则下载并安装适用于早期操作系统的 PowerShell。 请务必选择正确的操作系统下载。 请参见 Windows Management Framework 核心程序包 (bit.ly/9POYjq)。

无论您的操作系统是何版本,都需要下载并安装 WPK (bit.ly/dFVpfL)。 作为 Windows 7 资源工具包的一部分,它包含其他九个 PowerShell 模块,包括用于 PowerShell ISE 的 ISE 包。 PowerShell 版本 2 中提供了现成的 ISE。 ISE 包也是极好的学习资源,演示了如何在几个级别自定义 ISE。

启动 PowerShell 之后,运行此 cmdlet:Set-ExecutionPolicy RemoteSigned。 PowerShell 默认设置为不运行脚本;这是一种安全机制,PowerShell 用户需要重写此默认设置。 若要运行 Set-ExecutionPolicy,您需要具有管理员权限,并通过右键单击 PowerShell 程序文件并选择“以管理员身份运行”,从而以管理员身份显式运行 PowerShell。

从附带的代码下载中下载并解压缩脚本。 运行应用程序的最简单方法是使用 ISE。 在 Windows 7 上,可以单击“开始”,然后键入“ISE”。 (注意:不能通过双击操作运行 PowerShell 脚本(具有 .ps1 文件扩展名)。 运行示例脚本的最简单方法是启动 ISE,并使用“文件”|“打开”来打开脚本文件。)

PowerShell 101

我将构建一个现值计算器;这是一个简单的公式,如图 3 所示。

图 3 现值计算

PowerShell 中的变量以 $ 开头。 在第 7 行中,我直接使用 .NET Framework,以调用 System.Math 命名空间上的静态方法 Pow。 Pow 方法返回指定数字的指定指数幂。 调用静态 .NET 方法所需的语法是用方括号括起类,后跟两个冒号,然后是方法名:[System.Math]::Pow(2,3)。 如果在 ISE 中运行此代码,请按 F5(或单击“运行”按钮)以在输出窗格中查看结果。 也可以将此代码复制并粘贴到 PowerShell 命令行控制台中;它将运行并输出结果。

这是一个良好的开始,但这段代码的可重用性不高。 我可以继续键入新值并运行此脚本,但是我想从其他脚本调用它。 我将添加 function 关键字并将变量转换为参数,以便可以更改输出并使脚本具有交互性(请参见图 4 中的第 2 行)。

图 4 创建 PowerShell 函数

添加 Function 关键字

我将函数命名为 Get-PresentValue。 在命名函数时最好遵循 PowerShell“动词-名词”约定 — 这是 PowerShell 使用和开发中的基本概念。 它有预定义的谓词,如 Get、Invoke、New 和 Remove(键入 Get-Verb 可查看完整列表)。 试着键入 Get-Command;它会返回在 PowerShell 会话中定义的所有 cmdlet(以及更多内容),这是一个庞大的列表。

创建函数十分简单,只要键入 function Get-PresentValue {} 即可。 我将在此处创建参数,以及默认值和函数体,如图 4 所示。

比较一下图 3图 4,可以发现我将变量(名称和值)移到单行中,使用逗号分隔,用圆括号括起,并将其置于函数名称之后,从而使这些变量成为函数的参数。 这些参数现在具有默认值。 我原样保留了现值计算。 PowerShell 中的一个重要原则是“减少键入”。在图 4 的第 3 行中,我本可以使用 return 语句。 但在这里省略该语句可获得相同的行为。不过,有时您确实需要使用 return 语句来中断逻辑流。

在此示例中,我演示了调用 Get-PresentValue 函数的几种方法。 首先,不带参数,所有参数都是默认的。 参数可以按位置提供(请参见图 4 中的第 8 行),也可以进行命名(请参见第 9 行)。 建议您研究一下 PowerShell 参数;PowerShell 支持强大的参数绑定引擎。

接下来:更改 Get-PresentValue 函数以返回 .NET 对象而不是简单文本。

PowerShell 基于 .NET

PowerShell 中的一项重要创新就是以完全类型化对象形式传递数据的能力。 图 5 介绍了创建 PowerShell 对象、设置属性、返回该对象并利用 PowerShell 引擎将其输出的概念。

图 5 从 PowerShell 函数返回完全类型化对象

在第 6 行中,我使用了 cmdlet New-Object,它创建 .NET Framework 对象的实例。 我告知它要创建的对象类型 (PSObject),该类型可为 PowerShell 环境中的所有对象提供一致的视图。 同样是在第 6 行中,我使用了 -Property 参数,该参数采用一个哈希表。 PowerShell 中的哈希表简写语法是 @{}。 哈希表中定义的键/值对(在 -Property 参数中传递)会转换为新 PSObject 的属性和值。

最后,在第 15 行,我调用了这个函数,并且可以在输出窗格(ISE 中为蓝色背景)中查看结果。 请注意,PowerShell“知道”如何输出该对象。 我不必进行任何反射来确定要输出的属性或如何输出这些属性 — 这是 PowerShell 一个重要优点。

PowerShell 范围和管道

接下来,我使用 PowerShell 管道并介绍另外两个 PowerShell cmdlet:ForEach-Object 和 Format-Table(请参见图 6)。

图 6 PowerShell 范围和管道

第 13 行是耐人寻味的部分,您可通过它深入体会 PowerShell 的灵活性和可组合性。 此处有三个部分,定义了两个管道。 第一部分显示范围运算符(由两个句点构成),第二部分是 ForEach,最后一部分包含 Format-Table。 我将逐一讨论每个部分。

第一部分:1000..1010 1000..1010 表示从 1,000 到 1,010(包括这两个数字)的整数数组。 现在,PowerShell 开始将这些整数逐一推送到管道中(每当有一个整数可用,便立即推送)。 与基于 Unix/Linux 的 Shell 一样,PowerShell 也实现了管道,通过该管道可以将一个 cmdlet 的输出作为输入输送到另一个 cmdlet。 对于 PowerShell,该管道由 .NET 对象组成。 通过使用对象,便无需分析来自一个命令的任意文本输出以提取数据,因为所有对象都导出一致的接口(请参见 bit.ly/lVJarT)。

第二部分:ForEach { Get-PresentValue $_ }。此部分使用 ForEach(也可使用别名 %),它采用一个脚本块。 可将脚本块视为匿名方法(在其他语言中有时称为 lambda 表达式)。 有关此方面内容的更多信息,请参见 Bruce Payette 撰写的“PowerShell in Action, Second Edition”一书(Manning Publications 2011 年出版)。

$_ 是一个 PowerShell 自动变量,包含管道中的当前对象。 最终结果(包含 10 个整数的数组)会传递给 Get-PresentValue,一次传递一个整数。 因为我们未命名该参数,所以我将其作为位置参数传递给 $Amount,如图 6 中的输出窗格所示。

最后一部分:Format-Table。顾名思义,Format-Table 将输出设置为表的形式。 我使用了 -AutoSize 参数,因为该参数会根据数据宽度调整列的大小。

请注意,我没有管理对集合的遍历,并且 PowerShell“知道”如何输出通过管道推送的对象以及输出有关这些对象的哪些内容。 这样便会减少代码行的编写,也意味着调试的代码行更少。 因为我将 90% 的时间用于调试,而将另外 10% 用于编写 Bug,所以这样可大大加快我的进度。

GUI 出场 — 第 1 回合

该使用 WPK 了。 图 7 中的脚本生成图 8 中的 GUI。 我向 Get-PresentValue 函数的参数添加了类型信息(请参见图 7 中的第 1 行)。 这可方便使用此函数的其他人检测是否传递了错误数据(例如,传递了字符串而不是数字)。

图 7 WPK New-ListView

图 8 查看 GUI

这里保留了图 6 中原始代码的精髓部分,并向 ListView(这是提供用于显示一组数据项的基础结构的 WPF 控件)的 DataContext 添加了对 Get-PresentValue 的调用。 WPK 部分的其余内容与 WPF 数据绑定集成,并设置 ListView 中的视图,以便可以显示数据。

WPK 遵循 PowerShell 的基本原则,即使用“动词-名词”对的命名方式。 因此,如果我要创建 Window、Grid、Canvas 或 ListBox,则只需向其添加“New-”(即New-Window、New-Grid、New-Canvas 或 New-ListBox),这些框架元素已准备就绪,可供使用。

Import-Module

模块是包含可在 PowerShell 中使用的成员(如 cmdlet、脚本、函数、变量以及其他工具和文件)的包。 在导入某个模块之后,便可以在会话中使用该模块的成员。 如前所述,WPK 是 PowerShell 包的一部分,并且 WPK 包含 700 多个 PowerShell 函数,这些函数简化了在 PowerShell 上层实现 WPF GUI 的操作。

图 7 中的第 17 行和第 18 行来自传统的 WPF 背景,可能看上去有些与众不同。 WPK 通过提供两个表面参数来支持数据绑定:DataContext 和 DataBinding。 DataContext 参数采用一个脚本块,因此这里我传递一行 PowerShell 代码,这行代码创建在图 6 第 13 行中使用的 10 个现值对象。 接下来,我在第 18 行中设置要绑定的 ListView 的 ItemsSource 属性。 DataBinding 参数采用一个哈希表(请注意 @{})。 这些键是要绑定到的 GUI 对象的属性的名称。

WPK 有助于减少代码的编写。 New-GridViewColumn 函数采用一个字符串(例如 Amount,这是从 Get-PresentValue 函数发出的对象的属性名称),将其设置为标题,然后自动为您执行数据绑定。

我从一个简单函数 Get-PresentValue 开始,通过添加参数使其可重用,发出一个 .NET PowerShell PSObject 并利用 PowerShell 对象管道和范围运算符生成现值项,最终得到图 8 中所示的输出。 随后,我导入了 WPK,以便将 PowerShell 对象注入 WPF ListView 控件的 DataContext,绑定 ItemsSource 参数并创建 ListView 视图(以注入对象的属性名作为列名)。 最后,我得到了一个简单的 PowerShell/WPK 现值应用程序。 此应用程序很棒,但它是硬编码的。

接下来,我要与此应用程序进行交互,以便能够通过更改金额、利率和其他参数来考察投资情况。

GUI 出场 — 第 2 回合

要使用当前的 PowerShell/WPK 脚本,我需要更改参数、保存文件并重新运行脚本,这样做欠缺灵活性。

我将对它进行一些改造,以便能够从 GUI 调整五个参数,然后在 ListView 中显示新值。

New-Range 函数。首先,我要添加一个名为 New-Range 的函数(请参见图 9)。

图 9 New-Range 函数

PowerShell 提供的范围运算符不允许更改增量。 换言之,我不能指定 (1..10 by 2)。 而通过 New-Range 函数我可以自己指定增量,从而使“New-Range 1 10 2”输出 1 3 5 7 9。

接下来,我通过在 New-Window 函数中包装 New-ListView 来充实 WPK GUI。 单纯使用 New-ListView 时,WPK 也会提供一个包装窗口。 但指定 New-Window 可以提供更多控制(请参见图 10)。

图 10 New-Window 函数

我还将 DataContext 从 ListView 提升到窗口范围,并应用了新函数 New-Range。 现在我要添加五个文本框、五个标签和一个按钮。 这可以让我在保持应用程序运行的同时,调整 Get-PresentValue 的输出。 我将使用 WPK New-Grid、New-Label、New-TextBox 和 New-Button 函数创建所需的控件。 通过使用 New-Grid 创建网格控件,我可以灵活地调整窗口和控件的位置和大小。

为了对 GUI 进行布局,我打算在网格中嵌套网格及控件。 我仍在 DataContext 中使用 New-Range 和 Get-PresentValue 函数,如图 11 的第 2 行中所示。


(单击进行缩放)

图 11 New-Grid 函数

此外,New-ListView 仍存在于图 11 的第 30-37 行中。 我在第 30 行中添加了另外两个参数 –Row 和 –Column,这两个参数向 ListView 指示它应位于第 5 行所定义 New-Grid 中的哪一行和哪一列。 第 5 行中定义的 New-Grid 使用 Rows 和 Columns 对网格进行布局。 我需要两列(每列宽度为 75 像素)和三行(每行高度为 35 像素)。 Rows 参数中第二个 75 之后的星号指示它将占用所有可用空间。

现在,我在窗口中放置五对标签和文本框,并通过 Row 和 Column 参数向这些控件指示要定位到哪个网格象限。 此外,我命名了文本框以便以后进行访问,通过 -Text 参数向其提供默认值,并使用 Margin 和 HorizontalAlignment 参数对控件进行修饰。

最后,我使用 New-Button 函数在网格中放置按钮。 请注意,我在 Calculate 前放置了一个下划线。 这样,我便可以通过按 Alt+C 键来访问该按钮。

现在,这个现值计算器几近完成。 我必须将单击事件挂接到一些 PowerShell 代码,读取文本框中的值,将这些值作为参数传递给 Get-PresentValue 并将其绑定到 ListView。

Do-Calculation 函数

我将添加一个 Do-Calculation 函数,该函数只采用一个参数 $window(请参见图 12)。

图 12 Do-Calculation 函数

我通过 New-Button(内容为“_Calculate”的按钮)的 -On-Click 属性调用 Do-Calculation。

Do-Calculation 十分简单。 该函数获取所有文本框中的信息,将这些信息设置为 PowerShell 变量,然后将其作为参数传递给 New-Range 和 Get-PresentValue 函数。 我传递了参数 $window(请参见图 13 中的第 2 行),其中包含对图 10 中所创建的 New-Window 的引用。 使用此参数可以访问该窗口的所有属性以及子控件,具体而言就是文本框。 因为我命名了每个文本框控件,所以可以将 $window 通过管道输送到 Get-ChildControl,从而通过 .Text 属性传递控件名称并检索文本(请参见图 12 中的第 3-7 行)。


(单击进行缩放)

图 13 完成的网格

从文本框收集所有详细信息之后,我将 $window.DataContext 设置为通过管道输送到 Get-PresentValue 的 New-Range 的结果,这会生成一个 PowerShell 对象数组,其中包含 PresentValue 计算的结果(请参见图 12 中的第 9-12 行)。

图 13 演示了如何连接 Calculate 按钮的单击事件,以调用 Do-Calculation 函数并传递 $window 变量(请参见第 28-29 行)。 我添加了 -On_Click 参数,该参数采用一个脚本块。 我在其中调用 Do-Calculation 函数,同时传入在使用 New-Window 函数时创建的 $window 变量。 每次单击按钮时,都会重新计算屏幕。 请注意,在图 13 中还更改了第 2 行,也是为了调用 Do-Calculation 函数。

您可以在附带的源代码示例中下载完整的应用程序,以查看完整脚本。

一个简单的交互式 WPF 应用程序

我在此处展示了一个不足 75 行代码的脚本,这个脚本在 Microsoft 自动化平台 PowerShell 的上层构成了一个交互式 WPF 应用程序。 PowerShell 既是一种脚本语言,也是一个命令行控制台。 它通过与 .NET Framework 和 Windows 的深入集成,创造了激动人心的自动化机会。 当然,这也意味着使用者须花时间学习这一新平台。 好消息在于:您可以先采用简单的方式以提高工作效率,随后在您需要时,再深入研究 PowerShell 提供的大量自动化功能(来自 Microsoft 和一般 PowerShell 社区)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值