Click here to Skip to main content
15,884,537 members
Articles / Programming Languages / C#

Displaying a CodeDOM using WPF (Part 3)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
12 Nov 2012CDDL10 min read 36.8K   1.1K   35   12
Displaying a CodeDOM graphically using WPF

Introduction

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 using AsText() 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, CodeUnitVM, etc, and they have various Render() methods similar to the

AsText()
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 things. 

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 StackPanels for Blocks, and WrapPanels for “lines” of code, and TextBlocks for each piece of text. Here’s what the simple test method from ManualTests.cs looks like when rendered:

Image 1

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’, ‘continue’, ‘throw’, ‘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 display:

Image 2

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:

Image 3

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.

Image 4

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.

Image 5

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:

Image 6

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 WrapPanels, StackPanels, Borders, 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 of the CodeWriter 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 RenderVisible() 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. 

Summary

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.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions

 
GeneralMy vote of 5 Pin
Joezer BH10-Aug-13 23:43
professionalJoezer BH10-Aug-13 23:43 
QuestionJust perfect. Pin
aralmo16-Apr-13 5:25
aralmo16-Apr-13 5:25 
NewsWe're on the same page, check out my JS project Pin
keeyipchan16-Dec-12 13:43
keeyipchan16-Dec-12 13:43 
GeneralRe: We're on the same page, check out my JS project Pin
KenBeckett16-Dec-12 15:50
KenBeckett16-Dec-12 15:50 
GeneralJust awesome Pin
Guirec12-Nov-12 23:41
professionalGuirec12-Nov-12 23:41 
again...
I am sure You'll again have my vote of 5 for part IV. Give me 5mn to read it..
Seulement, dans certains cas, n'est-ce pas, on n'entend guère que ce qu'on désire entendre et ce qui vous arrange le mieux... [^]

I'd like a mug so vote!
Best C# Article October 2012
Best Overall Article October 2012

QuestionNice work Pin
NeverJustHere9-Nov-12 21:22
NeverJustHere9-Nov-12 21:22 
AnswerRe: Nice work Pin
KenBeckett10-Nov-12 6:13
KenBeckett10-Nov-12 6:13 
QuestionNova Studio Pin
F. Aro9-Nov-12 20:08
professionalF. Aro9-Nov-12 20:08 
AnswerRe: Nova Studio Pin
KenBeckett9-Nov-12 20:26
KenBeckett9-Nov-12 20:26 
GeneralRe: Nova Studio Pin
F. Aro10-Nov-12 9:42
professionalF. Aro10-Nov-12 9:42 
GeneralRe: Nova Studio Pin
tmms2-Mar-15 4:31
tmms2-Mar-15 4:31 
GeneralRe: Nova Studio Pin
KenBeckett3-Mar-15 19:43
KenBeckett3-Mar-15 19:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.