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

使用WPF显示CodeDOM(第3部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (15投票s)

2012年11月9日

CDDL

10分钟阅读

viewsIcon

38338

downloadIcon

1132

使用WPF以图形方式显示CodeDOM

引言

本文介绍如何使用WPF以图形方式渲染CodeDOM。文章中包含C# CodeDOM和类似IDE的基于WPF的应用程序的源代码。这是CodeDOM系列文章的第3部分。在前面的部分中,我讨论了“CodeDOM”的概况,并提供了C# CodeDOM的源代码

使用WPF渲染CodeDOM

我上一篇文章中的CodeDOM可以使用类中的AsText()方法将自身渲染为文本。添加图形渲染应该在这些类之外完成,作为它们之上的UI层。添加WPF UI的理想方式可能是使用MVVM,其中现有类是Model,用XAML实现的WPF UI控件是View,然后创建ViewModel类来连接这两者。然而,我选择了一种更快更粗糙的方法:我创建了一组映射到当前类的类 - 名称后缀为“VM” - 它们将来会成为ViewModel类,但我复制了现有的文本渲染代码,并基本上将其转换为创建WPF对象而不是文本。因此,有一个CodeObjectVM类,ExpressionVM,CodeUnitVM等,它们有各种Render()方法,类似于CodeDOM类中的AsText()方法。

以这种方式生成WPF对象,使得快速实现功能变得容易,也更容易尝试使用的WPF对象类型以及它们的组合方式。现在它似乎工作得足够好了,我真的需要花时间将其演变为完整的MVVM,但 alas,我一直忙于其他事情。

对于这个初始的样机UI,我选择在代码对象周围使用WPF Border对象,带有阴影的背景,颜色编码,文本组件也以某种标准颜色渲染。边框和文本对象在鼠标悬停时高亮显示。我使用WPF StackPanel来表示Block,使用WrapPanel来表示代码“行”,并使用TextBlock来表示每个文本片段。下面是ManualTests.cs中的简单测试方法渲染后的样子。

使用背景色时,它们不能太强烈,所以我选择了一些柔和的颜色,并尝试以一种合理的方式分配它们,以尽量避免相邻颜色过于频繁地相同。您会在此示例中注意到,绿色表示变量声明,浅蓝色表示条件语句,黄色表示表达式,浅红色(好吧,粉红色)表示“return”语句,因为它基本上改变了控制流(“break”、“continue”、“throw”、“goto”使用相同的颜色)。什么,你不喜欢边框、背景色和/或阴影效果?你可以(使用上下文菜单上的选项)关闭它们,以获得更典型的显示。

每个人都会有自己对美观的看法,而且我本人对目前的许多渲染选择并不是非常满意。最重要的事情是我们有了图形显示,并且任何都是可能的。实现一个带有缩放效果以及由广大用户群创建的可下载自定义皮肤和控件的真正出色的UI是最终目标,但这将需要一些时间。我的主要观点是,我们需要语言从一开始就提供这种能力,利用全球数百万开发者的创造力和力量,而不是依赖少数几个公司的人决定什么对我们最好 - 只有我们知道这一点(而且不是单色UI和全大写的菜单)。此外,当我们经历设计、编码、测试、调试和维护软件的不同阶段,以及转向非常不同的项目和针对不同平台时,我们对UI的需求实际上在不断变化。

自定义语言对象的UI控件有什么好处?您必须发挥想象力,但我敢肯定,如果我们能轻松做到这一点,我们会得到很多惊喜。如何使用一个“switch”UI控件,它会自动将简单情况格式化在单行上,或者甚至只显示一个类似Excel的带有From和To列的网格?如何使用一个UI控件,可以单击一次将复杂表达式展开为树状结构,以便使优先级显而易见,这样您就不必数括号了?

创建WPF“IDE”

仅仅以图形方式渲染代码是不够的——我们需要一个包含应用程序,它基本上会看起来像一个典型的IDE。我们想要一个典型的菜单和工具栏,每个代码文件(或片段)的标签,一个文件树,一个输出窗口等等。我已经把这个组合起来了,并称之为“Nova Studio”。这是它的一个(压缩版)截图,也显示了一个可选的文本视图代码。

可选的文本视图显示了代码对象本身作为文本发出的结果,以便与图形视图进行比较。许多菜单和工具栏项还没有功能(我们会逐步实现的)。右下角的小按钮是一个堆大小指示器,点击时还会强制执行GC,以便我们了解内存使用情况。顶部的字体和字号控件用于图形视图中的文本。

鼠标悬停在代码上时有工具提示,这些工具提示还可以嵌套。下面的示例(手动编辑以减小宽度)显示将鼠标放在MethodRef上,会得到一个描述该方法的工具提示,然后将鼠标移到“TestClass”上,得到描述它的工具提示,然后移到ArrayList上,得到它的工具提示。请注意,来自外部程序集中的类型会显示它们来自哪里——在本例中是“mscorlib 4.0”。您现在还不能导航到引用的对象,但这将在本系列的后续文章中介绍。

上下文(右键单击)菜单上有一些显示选项。源代码中的空白行对于组织代码行以提高可读性很重要,但它们真的必须全尺寸吗?让它们的高度是正常的一半,以便在屏幕上显示更多代码。让花括号变得很小,或者完全隐藏它们。隐藏分号、括号、“//”注释前缀,或隐藏所有注释。默认情况下,文档注释使用比例字体,而普通注释使用固定字体,因为它们有时是为了对齐而设计的。但是,如果您愿意,也可以选择为普通注释使用比例字体。

字面量的显示选项包括显示数字常量中的逗号,隐藏字符串中的引号,将字符串中的空格显示为可见字符,或显示字符串中的实际字符而不是转义序列。一个更不寻常的选项是使用标准符号来表示数学、相等和其他运算符,而不是传统的ASCII替代品,包括一个表示赋值的箭头(这使得“=”可以用于等价而不是“==”)。这是它在实践中的样子。

目前,它还为了好玩而将逻辑运算符更改为英语单词(如果您必须,可以恨我)。好吧,也许这不是很实用——我至少应该将它分解成多个单独的选项。但是,看到数学、集合和关系运算符就像您在学校学到的那样,是不是很酷?现代语言由于文本传统而受困于使用古老的ASCII符号——我们已经习惯了,但这并不意味着它不能更好(而且不用担心如何输入它们——您会输入ASCII版本,并在显示时进行转换)。您还可以选择如何想看到代码,同时保持与C#在读写文件时兼容,这也是很酷的。我还应该向那些母语不是英语的各位指出,我们可以轻松地允许所有关键字都用您的母语显示,即使它碰巧是中文或阿拉伯文。如果您不喜欢这些想法,那也没关系——每个人都可以选择自己想工作的方式。

阅读完毕后,请下载该应用程序并进行尝试(二进制文件包含在单独的ZIP文件中),因为小的屏幕截图无法充分展示它的效果。此时,您只能看到手动生成的“FullTest”代码,但请看看各种C#特性是如何渲染的,并尝试一下工具提示和显示选项。

UI的虚拟化

我在使用WPF进行此项目时学到的一个教训是,它使用一个庞大缓慢的对象模型。类中包含许多字段,其中许多是单独分配的子对象(即使是简单的数值通常也是双精度而不是整数)。对于基本UI,普通机器可能不会显示多少性能问题,但如果您需要显示大量数据,那么情况可能会变得非常缓慢。事实上,WPF为可能包含大量数据的控件提供了内置的“虚拟化”功能,通过在控件滚动时仅为当前可见项创建UI对象来处理此问题。

以图形方式渲染代码时,每个文本或符号都是这些沉重的WPF对象之一,它们包含在WrapPanelStackPanelBorder等中。这意味着每个C#源文件将有成千上万甚至数万个WPF对象,这可能需要几秒钟甚至更长的时间来生成,具体取决于文件大小。然后它会显示和滚动得很好,但设置延迟和内存使用量太大了。我通过实现自己的虚拟化版本来解决这个问题。基本思想是不要为当前屏幕外的对象生成WPF对象,而是在滚动发生时按需创建它们。我意识到水平渲染是有限的,不需要虚拟化,而我的Block类是垂直虚拟化的一个主要候选者,因为大多数代码“行”(除了语句头)都是Block的子项。

我在CodeRenderer(文本渲染的CodeWriter类的对应类)中添加了一个Measuring属性,该属性表示只进行测量,并使用它在第一个“pass”中确定正在渲染的所有对象的垂直大小。然后,我将Canvas的使用添加到BlockVM渲染逻辑中,进行了第二次实际渲染pass,该pass仅在Block中的项可见于滚动窗口时进行渲染,并在Canvas中使用位置渲染。当用户滚动时,有RenderVisible()方法会在对象进入或离开可见区域时创建和删除WPF对象。最终,这是一种出奇地简单而有效的方法,解决了性能问题。

使用附加源代码

所有“VM”UI渲染类都位于一个新的Nova.UI项目中,以及CodeRenderer类。 “IDE”位于一个新的Nova.Studio项目中,保持独立,以便未来的工具可以在不引入整个IDE的情况下以图形方式渲染代码。还提供了一个包含二进制文件的单独ZIP文件,以便您可以在不先构建的情况下运行Nova Studio。

摘要

现在我们可以以图形方式看到我们的CodeDOMs,并通过工具提示进行一些检查。也许您喜欢我做的第一个GUI尝试,也许您不喜欢……但它有助于看到对象关联(例如附加到其他对象的注释)。然而,手动构建CodeDOM对象树 certainly is tedious。有时能够做到这一点很重要,但大多数情况下,我们更愿意处理现有代码,这意味着是时候迈出又一个大步了(好像前两步不够大一样):解析。

在我的下一篇文章中,我将介绍一种“面向对象”的解析技术,将现有的C#源文件解析成CodeDOM对象。它并不是真正的传统解析方法,但它相对简单,速度快,最重要的是,它是可扩展的。

© . All rights reserved.