案例研究:重构 PowerBuilder 经典应用程序
创造性地思考 PowerBuilder Classic 功能通常可以为您提供可行的解决方案。
在强大的邦联火力下,身负重伤,被包围,损失了三分之一的兵力,联邦将军约翰·科尔斯正在美国内战的阿尔托纳战役的山顶上坚守阵地。他远眺,看到一面白色的信号旗在挥舞。这是谢尔曼将军的信号:“坚守阵地,我们正在赶来!” 救赎的念头极大地鼓舞了他,而敌人则因担忧一场大规模进攻而削弱了斗志,科尔斯将军和他的部下击退了邦联的攻击者。(请点击此链接了解更多关于这场战役的信息。)
成熟、功能丰富、市场领先的软件应用程序也可能“受到攻击”。攻击来自于市场上涌现的新应用程序,这些应用程序虽然功能和特性不如前者,但拥有现代化的吸引人的用户界面,并且符合行业的热门词汇。
史蒂夫·克鲁格在他的网络 UI 设计经典著作的四字标题中恰当地总结了 UI 体验:“别让我思考!” 最近,TactusMD (www.tactusmd.com) 聘请我和我的 eLearnIT 团队来重新设计他们功能丰富且屡获殊荣的 PowerBuilder 经典应用程序。TactusMD EHR 基于医生为医生设计的流程。通过其创新的触摸输入界面,TactusMD 减少了数据输入,让医生能够专注于病人,而不是病历录入。鉴于将复杂精密的应用程序迁移到 PowerBuilder .NET 所固有的挑战,我们必须在 PB Classic 框架内创建一个解决方案来刷新 UI。这次外观上的改进将满足用户对新 UI 的需求,同时开发者们也在计划并执行他们的 PB .NET WPF/Silverlight 迁移。TactusMD 应用程序既精密又复杂。很明显,这次外观上的改进需要一种精细的重构方法,以便在不干扰嵌入在事件处理程序和函数中的底层、交织在一起的 UI 和业务逻辑的情况下,更改 UI 层。请继续阅读,了解我们是如何做到的。
该 UI 被设计为在触摸屏上运行。所有导航、选择和大部分数据输入都在大型触摸屏上完成。几乎不需要键盘输入。用户触摸 DataWindows 中的 3D 凸起细节区域以进行选择,或触摸窗口控件的静态文本或命令按钮以显示选择列表或特殊样式的数字或字母键盘进行数据输入。图 1 显示了应用程序原始外观的截图。
图 2 显示了图形设计师渲染的建议外观。除了显而易见的主色调变化外,请注意以下几个独特特征:(1)选择列表项(左侧)是圆形的(2)按钮(3D 凸起静态文本)是圆形的(3)所有按钮都具有垂直渐变效果。
样式化窗口控件
如果应用程序已经迁移到 .NET 和 WPF,就可以编写一个优雅的 XAML 模板/样式表(也称为皮肤)并将其应用于应用程序以实现所需效果。不幸的是,项目时间和资源不允许进行 .NET 迁移。众所周知,动态样式和控件模板技术在传统的 Win32 中不存在。任何解决方案都必须是面向对象的,并且需要某种能够替换现有控件的基类。
经典 PowerBuilder 存在以下限制,必须绕过。(1)DataWindows 和窗口中的控件都是矩形的。(2)即使 DataWindow 对象支持颜色渐变,窗口和窗口控件也不支持渐变。(3)只有 DataWindow 对象控件支持透明背景色。
众所周知,面向对象的方法需要最少的编码。在这种情况下,基类控件需要支持可变的文本字符串和颜色、背景颜色和大小。总的来说,替换控件需要像标准的 PowerBuilder 窗口控件一样运行。
我寻找解决方案的过程引导我找到了 Paul Horan 的一篇博客,标题为“PowerBuilder 中的圆形按钮?” http://blogs.sybase.com/phoran/?p=67。在这篇博客中,Paul 演示了如何使用带有透明背景的图像文件来模拟 PowerBuilder 经典中的圆形按钮。Paul 演示的技术之一是将 png 文件用在外部 DataWindow 对象中。在我的例子中,图片控件不能替代静态文本和命令按钮,因为每个控件都有独特的文本、颜色和其他特征。但我的想法来了,为什么不使用一个外部数据源 DataWindow,其中包含一个带有叠加文本对象的圆形矩形来替换 3D 静态文本和命令按钮呢?
图 3 显示了根据图形设计师指定的调色板设计的 DataWindow 对象。
接下来,我构建了一个 DWControl 基类 `uo_rounded_edge_button` 来承载 DataWindow 对象。图 4 显示了该控件及其 API。`uo_rounded_edge_button` 类型将通过源代码编辑器替换 StaticText 和 CommandButton 控件。运行时将在创建时自动创建新的按钮类型。这里的目标是保留所有现有的事件和函数代码,只更改 GUI。
图 5 和图 6 显示了每个控件需要更改类型声明的两个地方。
在这里我遇到了一个麻烦。(你知道 snafu 是“Situation Normally All Fouled Up”的首字母缩写吗?)存在对静态文本控件属性的代码引用,而这些属性不是 DataWindow 控件的成员。为了绕过这些,我向 DataWindow 控件添加了一个实例变量,其名称与我正在替换的属性相同。但是——更改一个虚构的属性(如 Text)不会像更改系统定义的属性那样自动触发 GUI 行为。
为了让你理解这个挑战,请注意,经典 PowerScript 不支持定义和使用 .NET 风格的属性。.NET 属性本质上是一个私有实例变量,它被封装在 Get 和 Set 方法中。语法糖是,编码器只需要编写一个赋值语句。如果属性被赋值,则会自动调用 Set 方法。如果查询属性,则会自动调用 Get 方法。开发人员可以在 Set 和 Get 方法中编写任何必要的代码。(顺便说一句,PowerBuilder 12 .NET 中的 PowerScript 允许定义和使用属性。)
现在,让我们回到由赋值语句引起的、动态影响视觉变化的情况,例如 `Text= ‘Customer’`。由于我需要通过一个表达式来更改托管在 DataWindow 控件中的 DWO 内的视觉变化,我提出了用函数调用替换赋值语句的想法。例如,要更改按钮上的文本,我在 DataWindow 控件上编写了一个 `Text( )` 函数,该函数设置实例变量,然后修改覆盖文本对象上的文本属性。图 7 显示了该代码。
为了完成更改,所有属性赋值语句都必须重构为函数调用,将等号更改为括号,如下所示:
//st_assessment_type.text = assessment_type_description //changed to call st_assessment_type.text ( assessment_type_description )
调整窗口控件大小
请注意,这项技术将导致 DataWindow 控件的大小和位置与其替换的窗口控件相同,因为窗口实例化代码设置了控件的 x、y、height 和 width 属性,这些属性在 DataWindow 控件上存在且行为相同。然而,DataWindow 对象内的控件不会自动调整大小。要处理这个问题,我们需要在 DataWindow 对象构造函数中添加逻辑,该逻辑知道数据对象的控件。对于初始窗口实例化,我们可以通过在 DataWindow 对象构造函数中添加如下代码来实现:
This.object.rr_button.width = this.width - 30 This.object.rr_button.height = this.height - 30
您也可以在此处包含调整静态文本控件 t_label 的大小和位置的逻辑,但遵循每个级别所需信息最少的原则,您可以将所需的静态文本行为嵌入到为 Width 和 Y 属性设置的表达式中。这样,外部控件只需要知道并更改圆形矩形;DataWindow 对象内的控件将相对于圆形矩形自行调整大小和定位。例如,您可以为 `t_label` 的 `Y` 属性设置此表达式:
long(describe("rr_button.y")) + ((long(describe( "rr_button.height")) - long(describe("t_label.height"))) / 2)
这是 `Width` 属性的表达式:
long(describe("rr_button.width")) - 60
除了封装功能外,这项技术的另一个优点是,依赖的控件将自动调整自身,无论圆形矩形是如何或何时调整大小或重新定位的。也就是说,无论 `rr_button` 是在设计器中、通过属性表达式还是在运行时通过 PowerScript 调整大小,它都能正常工作。
如果 DataWindow 控件可能在运行时调整大小,那么您需要将上面的代码添加到 DataWindow 控件的 resize 事件中。这可能会导致代码运行两次,如果父窗口在 open 事件中调整 DataWindow 控件的大小,但如果窗口不调整控件大小,则控件将不会收到 resize 事件。运行两次不会造成任何损害,所以为了覆盖大多数情况,代码应该放在构造函数和 resize 事件中。
样式化选择列表
如上所述,应用程序的大部分数据输入是由基于 DataWindow 的选择列表驱动的。由于 DataWindow 具有先进的视觉能力,重新样式化它们相当直接。图 8 显示了原始系统中典型的选择列表 DataWindow 对象。虽然结果集描述可能包含许多列,但 GUI 在详细信息区域显示一个名为“Description”的项。
在调整了颜色和渐变外观后,定义了三种自定义颜色,用于设置背景和渐变颜色。首先设置了 DataWindow 背景颜色。然后将描述对象的背景颜色设置为透明,并将文本颜色设置为符合规范。接下来,在背景中放置了一个圆形矩形对象并调整了大小。最后,在圆形矩形上设置了渐变颜色,并编写了一个属性表达式,在窗口大小调整时将圆形矩形大小与描述大小同步。图 9 显示了修改后的选择列表 DataWindow。
图 10 显示了通过用 DataWindow 控件替换窗口控件并样式化 DataWindow 选择列表实现的最终外观。(*注意*:图标尚未更新。)
结论
虽然 PowerBuilder.NET 为现有的 PowerBuilder 应用程序提供了扎实的技术迁移路径,但移动大型应用程序所需的努力和规划可能会让人不禁思考:“如何才能利用 PowerBuilder DataWindows 的新特性来现代化应用程序的外观和感觉?” 对于已经大量使用 DataWindows 来驱动显示屏的应用程序,答案相对简单。然而,对于大量使用经典按钮和静态文本框的应用程序,一种间接的方法是使用智能 DataWindow 控件替换按钮和静态文本框,这些控件模仿控件功能,同时提供新的视觉特性。经过仔细考虑,DataWindow 控件的构建可以最大限度地减少转换工作量和对应用程序逻辑的影响。
当竞争对手发动攻击,您必须迅速做出回应时,请寻找远方挥舞的白旗。创造性地思考 PowerBuilder 经典特性,通常可以为您提供一个可行的解决方案,以加强和提升您当前的位置。