This article is about rendering a codeDOM graphically using
WPF. Sources for a C# codeDOM and
IDE-like WPF-based application are included.
This is Part 3 of a series on codeDOMs.
In the previous parts, I’ve discussed “CodeDOMs” in general, and
presented sources for a C# codeDOM.
Rendering a CodeDOM with WPF
The codeDOM from my last article can render itself as text
methods in the classes. Adding graphical
rendering should definitely be done outside those classes, as a UI layer on top
of them. The ideal way to add a WPF UI
would probably be to use MVVM, where the existing classes are the Model, WPF UI
controls implemented with XAML are the View, and View-Model classes are created
to wire those two together. However, I
chose a quicker-and-dirtier method: I created a set of classes that map to the
current ones – suffixed with “VM” on the names – that are destined to become
View-Model classes someday, but I then copied over the existing text-rendering
code and basically translated it to create WPF objects instead of text. So, there is a CodeObjectVM class, ExpressionVM,
etc, and they have various
Render() methods similar to the
methods in the codeDOM classes.
Generating WPF objects in this manner made it easier to get
things working quickly and also to play around with the types of WPF objects
being used and how they are put together.
Now that it all seems to be working well enough, I really need to find
the time to evolve it into full MVVM, but alas I’ve been kept busy with other
For this initial mock-up UI, I chose to use WPF Border
objects around code objects, with shaded backgrounds that are color-coded, and
text components are also rendered in somewhat standard colors. The borders and text objects highlight on
mouseover. I used WPF
for “lines” of code, and
TextBlocks for each piece of text. Here’s what the simple test method from
looks like when rendered:
When using background colors, they really can’t be too
strong, so I came up with some pastels, and tried to assign them in a
reasonable way that would try to avoid adjacent colors being the same too
often. You’ll notice in this example
green for variable declarations, light blue for conditionals, yellow for
expressions, and light red (OK, pink) for the ‘return’ statement because it’s
basically a change in the flow of control (the same color is used for ‘break’,
‘goto’). What, you don’t like the borders, background
colors, and/or shading effect? You can
just turn them off (using options on the context menu) for a more typical
Everyone will have their own ideas on what looks good to
them, and I’m not all that in love with a number of my own rendering choices so
far. The most important thing here is
that we have a graphical display, and anything
is possible. Getting to a really awesome
UI with zooming effects and downloadable custom skins and controls created by a
large user base is the ultimate goal, but that will take some time. My main point is that we need languages to provide
this sort of capability from the beginning,
harnessing the creativity and power of the millions of developers in the world
instead of depending upon a handful of people at one company deciding what is
best for us – only we know that (and
it’s not monochrome UIs with
all-upper-case menus). Also, what we
need in a UI is really constantly changing as we move through the different phases
of designing, coding, testing, debugging, and maintaining software, and also
move on to very different projects and targeting different platforms.
What is the benefit of custom UI controls for language
objects? You have to use your
imagination, but I’m sure if we made it easy to do we would get lots of nice
surprises. How about a ‘switch’ UI control
that automatically formats simple cases on single lines or even just displays
an Excel-like grid with From and To columns?
How about a UI control that can expand complex expressions into a tree
structure with one click in order to make precedence obvious so you don’t have
to count parentheses?
Creating a WPF “IDE”
Rendering code graphically isn’t enough by itself – we’re
going to need a containing application that basically ends up looking like a
typical IDE. We want a typical menu and
toolbar, tabs for each code file (or fragment), a tree of files, an output
window, etc. I’ve put something like
this together and christened it “Nova Studio”.
Here’s a (crunched-down) screenshot of it, which also shows an optional
text view of the code turned on:
The optional text view shows the result of the code objects
emitting themselves as text, so that it can be compared to the graphical
view. Many of the menu and toolbar items
aren’t functional yet (we’ll get there).
The little button in the bottom right is a heap size indicator that also
forces a GC when you click on it, so we can get an idea of memory usage. The font and point size controls at the top
are for text in the graphical view.
There are tooltips as you mouse-over code that are also
nestable. The example below (manually edited
to reduce the width) shows putting the mouse on a
MethodRef, which gives a
tooltip with a description of the method, then moving the mouse to ‘TestClass’
and getting a tooltip describing that, then moving to
ArrayList and getting
a tooltip for that. Notice that types
from external assemblies show where they’re from – in this case ‘mscorlib 4.0’. You can’t navigate to referenced objects just
yet, but that’s coming later in this series.
There are a number of display options on the context (right-click)
menu. Blank lines in source code are
important to group code lines for readability, but do they really have to be
full size? Make them half the normal
height to fit more code on the screen.
Make braces tiny, or hide them altogether. Hide semi-colons, parens, the ‘//’ prefixes
on comments, or hide all comments. By
default, doc comments use a proportional font, and regular comments use a fixed
font since they are sometimes designed to line up. But, you can also choose to use a
proportional font for regular comments if you like.
Display options for Literals include the ability to show
commas in numeric constants, hide the quotes on strings, make spaces in strings
visible characters, or show actual characters in strings instead of escape
sequences. A more unusual option is the
ability to use standard symbols for math, equality, and other operators instead
of the traditional ASCII substitutes, including an arrow for assignments (which
frees ‘=’ to be used for equivalence instead of ‘==’). Here’s what that looks like in action:
For now, it also changes the logical operators to English words
just for fun (hate me for it if you must).
OK, perhaps this isn’t that practical – I should at least break it up
into multiple separate options. But, isn’t
it cool to see the math, set, and relational operators like you learned them in
school? Modern languages are stuck using
ancient ASCII symbols because of the text tradition – we’re used to it, but
that doesn’t mean it couldn’t be better (and don’t worry about how you would
type those in – you’d type the ASCII version and it would convert on display). It’s also cool that you can choose how you want to see the code, while
remaining compatible with C# when you read and write files. I should also point out to all of you for
whom English is not your first language that we could easily allow for all
keywords to be displayed in your native tongue, even if it happens to be
Chinese or Arabic. If you don’t like
these ideas, that’s fine – everyone would have the option to choose how they
want to work.
When you’re done reading, please download the app and try it
out (binaries are included in a separate ZIP file), because small screenshots don’t do it justice. At this point, you only have the manually generated
“FullTest” code to look at, but take a look at how the various C# features are
rendered, and play around with the tooltips and the display options.
Virtualization of the UI
One of the lessons I learned while using WPF for this
project is that it uses a big and slow object model. The classes are stuffed with many fields, and
many of those are separately allocated child objects (even simple numeric
values are usually doubles instead of integers). A typical machine probably won’t show much of
a performance issue with basic UIs, but if you have a lot of data to display then
things can get really slow. Indeed, WPF has
built-in “virtualization” capability for controls that might contain a lot of
data, where it handles this problem by only creating UI objects for currently
visible items as the control is scrolled.
When rendering code graphically, every piece of text or
symbol is one of these heavy WPF objects, and they are contained within
etc. This means many thousands or even
tens of thousands of WPF objects per C# source file, and this could take
several seconds or longer to generate, depending upon file size. It would then display and scroll just fine,
but the setup delay and memory usage are just too much. I solved this problem by implementing my own
version of virtualization. The basic
idea is to not generate WPF objects for things that are currently off-screen,
creating them on demand as scrolling occurs.
I realized that horizontal rendering was limited and didn’t need to be
virtualized, and that my Block class was a prime candidate for vertical
virtualization since most “lines” of code (other than statement headers) are
children of a Block.
I added a Measuring property to
CodeRenderer (the counterpart
class used for text rendering) that indicates that only measurement is being
done, and used this to determine the vertical sizes of all objects being
rendered during a first “pass”. I then
added the use of a
Canvas to the BlockVM rendering logic, doing a second
real rendering pass that only renders items in Blocks if they are visible
in the scrolling window, using positional rendering within the Canvas. When the user scrolls, there are
methods that create and remove WPF objects as they move into or out of the
visible area. In the end, this was a
surprisingly easy and effective technique that solved the performance issue.
Using the Attached Source Code
All of the “VM” UI rendering classes are in a new Nova.UI
project, along with the
CodeRenderer class. The “IDE” is in a new Nova.Studio project,
kept separate so that future tools can render code graphically without dragging
in the entire IDE. A separate ZIP file containing binaries is also provided
so that you can run Nova Studio without
building it first.
Now we can see our codeDOMs graphically and inspect them a
bit with tooltips. Maybe you like my first
attempt at a GUI, and maybe you don’t … but, it helps to see the object
associations (such as comments that are attached to other objects). However, building up trees of codeDOM objects
manually is certainly tedious. Being
able to do that at times is important, but more often than not we’ll probably
be wanting to work with existing code, and that means it’s time to take yet
another big step (as if the last two weren’t big enough): Parsing.
In my next article, I’ll present an “object-oriented”
parsing technique to parse existing C# source files into codeDOM objects. It’s not really the traditional method of parsing,
but it’s relatively simple, it’s fast, and most importantly, it’s extensible.