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

  • Post author:
  • Post category:其他



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 社区)。



版权声明:本文为wangyong0921原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。