65.9K
CodeProject 正在变化。 阅读更多。
Home

将新技术融入现有代码行

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2011 年 10 月 20 日

CPOL

7分钟阅读

viewsIcon

8423

PowerBuilder 有一个特定的代码问题:可视化继承层次结构及其 XAML 实现,它们会极力抵抗任何更改。在本文的其余部分,我将重点讨论这个问题。

如果你不知道这首经典民歌的歌词,这里是:

哦,老花花马,她已不如当年,不如当年,不如当年。老花花马,她已不如当年,许多年前.

Merging-Newer.jpg

我不知道听了多少次这首歌的旋律,背景是一个动画卡通片。通常,它以缓慢、夸张的节奏播放,以配合一个疲惫不堪的角色拖着步子前进,显然被某种麻烦缠身。(如果你小时候听的是不同的旋律,可以在这里查看:http://freekidsmusic.com/traditional/the-old-gray-mare.html)由于这种卡通化,我给这首旋律打上了负面烙印。然后我发现了‘真实’的故事。根据维基百科的说法,“有些作者说这首歌是基于 Lady Suffolk 的非凡表现而创作的,她是第一匹在两分半钟内跑完一英里的马。这发生在 1843 年 7 月 4 日,在新泽西州霍博肯的 Beacon Course 赛马场,当时她已经 10 岁了。”哇,一匹十岁的马打破了世界速度纪录。很厉害!这与 PowerBuilder 有什么关系呢?很多!你很快就会看到!

背景

我刚刚写完了一份关于使用 PowerBuilder 12 .NET 进行 WPF 布局的富媒体电子教程。该教程指导经典的 PowerBuilder 开发人员掌握使用 XAML 的声明式特性构建流畅 WPF 布局的细微差别。WPF 拥有强大的功能,可以用来改造你的用户界面。它真是一匹强大的赛马。但是(而且是一个巨大的‘但是’),WinForm 和 WPF 是不同的技术,并且有不同的呈现策略。WinForm 是基于坐标的。控件的位置由 x 和 y 坐标指定。控件的大小是绝对的,有给定的高度和宽度。在性能方面,每次控件移动时,整个屏幕都会被重绘。借鉴出版业的经验,WPF 是基于布局的;重点是创建灵活、流畅、适应性强且与屏幕分辨率无关的布局。在 WPF 中,基于流的布局是标准的;经典的基于坐标的布局仅在 Canvas 布局形式上得到基本支持,该布局近似于绝对定位。屏幕渲染速度很快,因为 WPF 建立在 DirectX 技术之上。只有屏幕上受移动或更改影响的部分才会被重绘。

当你将你的经典应用程序迁移到 .NET WPF 时,PowerBuilder 会以刚性的 Canvas 布局渲染你的窗口和 CVUO。如果你的应用程序有一个自制的 resize 服务,或者依赖于行业标准的 PFC 服务,你的窗口将继续以流畅的方式调整大小,尽管是以代码为基础而不是原生的声明式方式。但如果你的经典窗口在调整大小时遇到困难,你可能会倾向于迁移到流畅、灵活的显示容器。你可能会对自己说,“嘿,我只需要更改几个 XAML 标签就行了。”消息是,这种方法存在两个固有的缺陷。首先,在进行任何激进的 GUI 修改之前,必须考虑视觉设计。你需要弄清楚你的 GUI 元素应该如何响应大小和分辨率的变化,并以逻辑的方式设置你的元素。你可以通过我的电子课程或任何好的 WPF 书籍来学习这个非语言/平台特定的设计问题。其次,有一个 PowerBuilder 特定的代码问题:可视化继承层次结构及其 XAML 实现,它们会极力抵抗任何更改。在本文的其余部分,我将重点讨论这个问题。

可视化继承与布局

XAML 的设计初衷并非支持继承。XAML 是一种声明式标记风格语言。它基于数据和业务逻辑与表示的分离。它不是一种面向对象的代码驱动方法(尽管 XAML 确实会编译成代码对象运行时调用)。重构 XAML 的方法是通过资源字典、模板绑定等。你可以动态地和静态地应用资源字典。(我将构建电子教程来展示如何做到这一点。)

由于 Classic PowerBuilder 完全支持可视化继承,并且大多数应用程序都严重依赖它,PowerBuilder .NET 添加了对可视化 XAML ‘继承’的开发和编译时支持。然而,正如你很快会看到的,由于它依赖于 Painter 的动态代码生成和一些专有的内部格式,它很难改变。在编译时,当一个后代窗口在 painter 中打开时,后代 XAML 会通过合并层次结构中的所有 XAML 来动态生成。让我们看看 PowerBuilder 是如何做的。

通过示例进行 XAML 合并

我创建了一个经典的应用程序,然后将其迁移到 .NET 以说明这些问题。图 1 显示了应用程序架构。图 2 显示了解决方案资源管理器中的目标。它有一个 CVUO (u_bar),一个由按钮栏、一个 DataWindow 控件、一个图片和一个命令按钮组成的祖先窗口。最后,有一个后代窗口,它添加了另一个 DataWindow 控件和另外两个按钮。

Yakov-fig-1_0.jpg

Yakov-fig-2_0.jpg

Yakov-fig-3_0.jpg

从顶部开始,让我们看看生成的 XAML。列表 1 显示了为 CVUO u_bar.sru.xaml 生成的 XAML(为简洁起见,省略了命名空间声明)。

图 4 显示了用户对象 painter 中的相同 XAML。很明显,painter 中的属性比 XAML 文件中的多得多。这是为什么?

Yakov-fig-4_0.jpg

现在让我们向下移动一级到祖先窗口。列表 2 显示了生成的 XAML 文件 w_anc.srw.xaml。我将用户对象的 XAML 加粗了。请注意,它只是一个占位符。用户对象的 XAML 大部分都不在这里!

图 5 显示了 WPF 窗口 painter 中的相同 XAML。你可以清楚地看到用户对象的 XAML 已合并到窗口中。我的天,painter 的魔力!

Yakov-fig-5_0.jpg

现在让我们下移到下一级,后代窗口。列表 3 显示了 XAML。再次注意,祖先 XAML(加粗部分)仅仅是一个占位符。这次占位符向上延伸了两级祖先:基窗口和用户对象。

图 6 显示了 WPF 窗口 painter 中的相同 XAML。同样,它已完全展开。

Yakov-fig-6_0.jpg

注意几个关键问题。(1) 你可能会认为你可以更改窗口中的任何 XAML。但实际上,你不能。如果你使用 WPF 编辑器修改后代 XAML 并对祖先 XAML 进行更改,你可能会认为你完成了某项工作,但当你保存时,你所有的更改都会丢失(没有任何警告!)。(2) 更糟糕的是(至少在 build 5530 中),由于继承问题,你一些善意的后代更改会混淆 XAML 编写过程,并导致你的 XAML 渲染不正确。最终结果是——下次当你打开你的窗口时,布局编辑器会告诉你它无法加载 XAML。然后你将被迫弄清楚保存时发生了什么,并手动修复它。

怎么办?

在与一些产品工程师的交流中,我提出了更改 XAML(特别是从 `` 到 ``,但这适用于任何根本性更改)的问题。以下是他们提出的建议:

“如果涉及继承,最佳建议是 *在 IDE 外部* 更改后代及其所有祖先的 XAML 文件中的根布局面板,使其保持一致,然后再在 IDE 中重新打开后代。目的是通过保持一致性来尽量减少对底层合并/差异算法的影响。”

然而,Canvas 和 Grid 非常不同,因为你将从一个绝对定位系统转到一个基于 Margin 的定位系统。之后,你将不得不将所有 constituent 控件的 Canvas.Left 和 Canvas.Top 属性更改为 Margin 属性,最好从祖先开始,然后向下进行。”

我的反应是:“唉,在没有语法和工具支持的情况下,在 painter 之外工作;在不清楚写入后代的局部祖先 XAML 的情况下。多么耗时、容易出错的方法!绝不可能!”

经过仔细考虑,我得出的结论是,从头开始可能更好(也就是说,痛苦更少)。创建一个新的基窗口层次结构。将所有通用的、核心的窗口功能构建(或连接)到其中。这个基窗口应该没有任何控件。给它一个 `` 布局容器。然后,对于每个实现窗口,都继承自这个基窗口,并在具体级别上设计一个精心设计的 `` 来样式化每个用户界面。

结论

老花花马真是一匹强壮的赛马。有一只强大的野兽等待释放。但是,在处理遗留系统时,这匹马承载着沉重的负担。将新技术融入现有代码行需要仔细的考虑和深思熟虑的方法。

© . All rights reserved.