Introduction

PlantUML Editor, built using WPF and .NET 3.5, is an IDE for drawing UML diagrams using the amazing PlantUML tool. If you have used PlantUML before, you know you can code UML diagrams super fast without struggling with a designer environment. Especially those who use Visio to draw UML diagrams (God forbid!), you will be in heaven. This is a super fast way to get your diagrams up and ready for show. You can *write* UML diagrams in plain English, following a simple syntax and get diagrams generated on-the-fly.

This editor really saves time designing UML diagrams. I have to produce quick diagrams to convey ideas quickly to Architects, Designers and Developers everyday. So, I use this tool to write some quick diagrams at the speed of coding, and the diagrams get generated on the fly. Instead of writing a long mail explaining some complex operation or some business process in English, I can quickly write it in the editor in almost plain English, and get a nice looking activity/sequence diagram generated instantly. Making major changes is also as easy as doing search-replace and copy-pasting blocks here and there. You don't get such agility in any conventional mouse-based UML designers.

How Does It Work

Here's a quick screencast of how it works:

How To Install It

First install GraphViz. Then create an environment variable named GRAPHVIZ_DOT that points to the location where GraphViz has stored the dot.exe. For example, C:\Program Files (x86)\Graphviz2.26.3\bin\dot.exe.

Graphviz is used by plantuml to render the diagrams.

Then you can just download the installer that I made and install it. It has plantuml Java jar embedded as well.

The Code

It's a single WPF project with some utility classes that you can reuse in your projects. The Utilities has a BackgroundWork class which is used to perform background tasks in WPF. It's more powerful than .NET's default BackgroundWorker component or Dispatcher because you can get more control over how the background tasks run, wait until all background work is finished, abort a work, etc. I will write about the BackgroundWork library separately.

The MainWindow.xaml is the main window that you see on screenshot. It hosts the left side file list and the right side welcome panel. The editor and the diagram image is in the DiagramViewControl.xaml that takes care of showing and editing of a particular diagram.

This is my second WPF app I ever made, so have mercy.

Let's look at the cool stuff to take out from the code.

The Theme

You must be salivating to get your hands on the theme for this project. I was too. I must say I have taken a lot of inspiration (and code copy and paste) from the famous Family.Show project and tailored it to fit the need. Then I got the background image from some example of Expression Blend.

You should look at the Skins\Black\BlackResource.xaml to get an idea of how such custom themes are built. The amount of wisdom in this file is mind boggling.

Listbox with Preview of File Content and Diagram

The left side listbox shows a preview of content from the actual file and the generated diagram. First, you have to define a custom template for ListBox items:

Here the ListBox's ItemSource attribute is bound to a design time data source that I will talk about soon. Basically it is bound to a collection of DiagramFile class which holds information about a diagram file - the path, path to the diagram image, preview of the actual content, etc.

The Image is bound to the path of the diagram image. In order to preview the diagram, I had to create a custom converter. Otherwise the Image holds a lock on the image file and does not allow it to be changed. Here's the code of the converter:

It solves two problems. First, it does not hold a lock on the file so that the file can be changed while you edit the diagram. Secondly, it looks like WPF has some kind of internal cache and it holds the image in the cache. You cannot refresh the image and make it load from source. So, you have to do the BitmapCreateOptions.IgnoreImageCache trick to make it work.

On the code behind, the grid is loaded in a background thread when the application starts. Also when you change the path in the top-left corner location box, the grid is refreshed. It looks for all the text files in the given folder and checks if any of the text files is in plantuml format. If it is, then it loads it in the list.

Here you will see one use of my BackgroundWork class. I am using it to load the files from the given path in a separate thread so the UI remains responsive. The first callback is for doing the work on a separate thread. Here I am building a new collection of diagram files. I cannot change the existing collection which is already bound to the ListBox because that would trigger an update on the ListBox and .NET would not allow that to happen from a seaprate thread. So, once the new collection is built, the second callback is fired which is the onSuccess callback. It executes on the UI thread and here I can re-bind the ListBox.

Here's the DiagramFile class which holds information about each diagram:

Now this is loaded at run time. How does the design time environment show realistic data in the ListBox?

Live Rendering of Data in Design Time inside ListBox

In WPF, you have to create a ObservableCollection<YourClass> and then create a StaticResource out of it to show the result of binding at design time. For example, here I am showing the result of binding diagrams to the ListBox and the tabs:

Here's how you do it. First create an ObservableCollection out of your entity class and in the constructor, populate it with dummy data.

That's it. You can then bind ItemSource attribute of controls to this static resource.

Smooth Auto Collapse Grid Column to Give More Space

You might have noticed that when you go into editing mode by clicking on the diagram text box, the left column smoothly collapses to give you more editing space. You can do this by using a custom animation to reduce the width of the left column.

Then create two Storyboards, one for collapsing and one for expanding. You will have to create a custom animation here because by default there's no animation class available to animate a Grid's Width or Height property which are of GridLength data type. I have used the great example from this article, which saved a lot of time.

These animations are triggered from the code. Whenever you click on the editor text box, the GotFocus event gets fired on the usercontrol that wraps the text box. On the event, I start the animation. Similarly when you leave the editing environment and click on the ListBox, the LostFocus event is fired and I run the Expand animation.

As you see, it's not straightforward. When the column is collapsed, you need to remember the Width of the column before you start collapsing, so that you can restore it to its original width when it is expanded.

The Cool Tabs

The default tabs in WPF are so uncool. As soon as you put the TabControl on your form, your hard effort in making amazing UI goes to the drain. So, I have customized the appearance of the tabs using custom templates.

Next, like the ListBox, the TabControl is bound to the same design time collection, so that you can see how the tabs look like on the design view. Inside the TabControl, the DiagramViewControl is hosted, which delivers the editing and preview features for a single diagram. The DataContext property of the DiagramViewControl is mapped to the SelectedItem property of the tabs so that it gets the active tab's associated DiagramFile object. So, if you change tab, the DataContext property of DiagramViewControl changes as well to show the correct tab's data.

Notice the two way binding. It's because any changes made inside the DiagramViewControl on the content is reflected to the associated DiagramFile object as well.

Now you will notice, when you double click a file on the ListBox, a new tab is created dynamically to show that particular diagram. At run time, the TabControl is attached to another ObservableCollection<DiagramFile> called OpenDiagrams. All you need to do is, add a DiagramFile object to this collection and a new tab pops up. Joy of data binding!

Diagram Editor - Powerful Code Editor

The code editor uses the ICSharpCode.AvalonEdit control, mostly because it supports block indenting, which is not available in WPF's default text editors. Using this control is pretty straightforward. Add the reference to the DLL, declare the namespace inside <UserControl> tag and then use it like this:

That's it. The only caveat is it does not support binding the Text property to a string. So, I could not find it to the DiagramFile.Content property to show and reflect changes in the text. So, I did this from the DataContextChanged event of the UserControl, which gets fired whenever the DataContext is changed to some other object.

This keeps the DataContext of the text editor control in sync with the DataContext of the usercontrol.

Diagram Image Preview with Zoom Support

You can use the Image control in WPF to show an image from a local file, from a URL or from a MemoryStream. The Image control is super powerful. It has complex transformation support. You can zoom, twist, transform images many ways. You can also bind the transformation to other WPF controls. For example, you can bind a Scaling transformation to a slider, so that when you slide the slider up and down, the image zooms in and out.

Here you see, I have bound the ScaleTransform to the ZoomSlider control's value.

Whenever you save the diagram, it's generated again using plantuml and then the diagram image is refreshed. Since we have to use a custom value converter to bind the image to a path, we miss the auto refresh ability. The Image control no longer listens on the file for change. So, we have to force a refresh on the image using custom code:

You can read the comments to understand the pain I went through and you won't have to go through it anymore.

Diagram Image Show in Explorer

Since it is a simple trick, but worth mentioning. You can right click on the image and select "Show in explorer" which would launch Windows Explorer where the image is already selected. You can immediately copy and paste the file or do any other file operations. This is done by this:

Auto Refresh and Render Diagram in Background

When you keep typing on the code editor, it keeps refreshing the diagram every 10 seconds. This is where my BackgroundWork class comes handy. I can check if any background work is already running. If not, I can queue a work to happen after 10 seconds, in a separate thread.

First it waits for all background work to complete. Then it starts running the planuml process in a separate thread. The program is run without any window so that it does not take the focus away from your code editor. When the diagram is successfully generated, the diagram image is refreshed.

Running Java Program as EXE

I have used the fantastic JSmooth utility to create an EXE out of the plantuml jar. It has sophisticated JVM detection ability built-in. The EXE wrapper it creates detects the right JVM and then runs the jar.

You can embed the jar inside the generated EXE to create a single EXE file that automatically extracts and runs the jar.

Show the Right ContextMenu Automatically from a Button Click

That's not it. It also remembers which submenu you clicked the last time and it automatically opens that submenu saving you one click. When you are on a use case diagram and adding use case elements, it's likely you will keep on adding use case items. Clicking on "use case" and then selecting an item from it is cumbersome. The intelligent context menu saves you from making that unnecessary click.

privatevoid AddStuff_Click(object sender, RoutedEventArgs e)
{
// Trick: Open the context menu automatically whenever user
// clicks the "Add" button
AddContextMenu.IsOpen = true;
// If user last added a particular diagram items, say Use case
// item, then auto open the usecase menu so that user does not
// have to click on use case again. Saves time when you are adding
// a lot of items for the same diagram
if (_LastMenuItemClicked != default(WeakReference<MenuItem>))
{
MenuItem parentMenu = (_LastMenuItemClicked.Target.Parent as MenuItem);
parentMenu.IsSubmenuOpen = true;
}
}

Here the context menu is programmatically opened by using IsOpen property. However, right after opening it, I am checking what was the last submenu item that was clicked and I open that automatically as well.

If you look at the XAML for the button, you would notice, there's no click handler. There are so many submenus and grand-child menus that it's hard to add a event handler from the markup.

Now those who know me, you know very well that I am careful about strong refs and memory leaks. I would not do such nasty things like holding reference to an UI element in a private variable, or trying to access controls at parent scope from anonmous delegates. That's evil! So, I use strongly typed WeakReference<T> class to hold reference to UI elements and pass the weak refs to the delegates.

For example, you can see use of weak reference to anonymous delegates:

I have a handy WeakReference<T> class at my disposal which supports implicit conversion to and from any class. It makes it easy to use the reference as if it can take any class and can become any class. As you see in the above code example, there's no cast to ListBox data type anywhere. It behaves almost like an implicit pointer.

So, here it is...

The Strongly Typed WeakReference<T> with Implicit Operator Conversion

I have taken this from the great Damien's blog and made some enhancements to it:

I have added the implicit operator conversions to make use of WeakReference<T> more convenient.

Stuffing Lots of Data in MenuItem

You must have noticed my clever use of Tag property? If not, then here it is again. I am storing the UML snippet inside the Tag of each menu item this way:

<MenuItemStyle="{DynamicResource MenuItemStyle}"Header="Note beside usecase"><MenuItem.Tag><![CDATA[
note right of (Use)\r
A note can also\r
be on several lines\r
end note
]]></MenuItem.Tag></MenuItem>

When a menu item is clicked, I can read the UML snippet from the Tag and then use it to inject into the code editor. I am using Clipboard to copy the UML snippet and then paste it inside the editor. This way, it preserves the indenting.

Auto Updater

What's a smart client without an auto updater? When you launch this app, it starts a background thread to check if there's any update submitted for this app. Now this application is uploaded into Google Code site. So, I need to check if there's a newer version of the installer file. I do this by making a Web Request to the download URL and check the Last-Modified response header. Then I compare it with the last modification date of the EXE running on your computer. If they are off by one day, then we have an update.

When the download is in progress, the DownloadProgressChanged event is fired and when the download completes/cancels, it fires the DownloadCompleted event. The completed event handler takes care of finishing the installation.

When the download completes, it runs the installer and quits the application so that the installer can install the latest version over it.

The Installer

Visual Studio still does not offer creating setup projects which produces a single EXE or MSI file which has a bootstrapper to detect .NET Framework version, download and install if needed and then run the real MSI. So, I had to use the famous dotnetInstaller. But getting it to detect .NET Framework 3.5 was a challenge. It does not come with built-in support. Moreover, creating a single EXE with self-extracting installer is not so straighforward. Here's my take.

First you have to add a custom component to your configuration file in order to get .NET Framework detected. The solution is given here.

Then you have to add an MSI component that points to the MSI file. But there's a trick to the file path.

You have to provide the path in this format. And you have to add an Embed File underneath the .msi component you added and set the path exactly like this:

Notice the targetfilepath is blank.

Then you need to create a folder, where you will copy the dotnetinstaller.exe file, the .msi file. Both of the files need to be in the same folder. And then make the project to prepare the single installer.

Conclusion

This editor really saves time designing UML diagrams. I have to produce quick diagrams to convey ideas quickly to Architects, Designers and Developers everyday. So, I use this tool to write some quick diagrams at the speed of coding, and the diagrams get generated on the fly. Instead of writing a long mail explaining some operations or some process in English, I can quickly write the text in the editor and get a nice looking activity/sequence diagram produced. Making major changes is also as easy as doing search replace and copy-pasting blocks here and there. You don't get such agility in any conventional mouse-based UML designers. Hope you find this tool useful and if you do, please spread the love.