top of page

Simple DVH Summary Script using WPF and MVVM


If you're going to write an ESAPI script with a graphic user interface (GUI), I recommend using the Windows Presentation Foundation (WPF) and the Model-View-ViewModel (MVVM) pattern.

WPF is a technology for writing GUIs using a language called XAML. The MVVM pattern helps you to keep the presentation logic (the GUI) separate from the business logic. This makes your programs more maintainable and easier to test.

There are many (probably hundreds) of tutorials online on WPF and the MVVM pattern, so I'm not going to teach you these here. Instead, I'm going to walk through a simple script using WPF and MVVM. In addition to Stack Overflow, here's a list of some of the resources I used to learn these technologies:

Using MVVM, you don't access the controls (or views) directly, like a button or text box. Instead, you get or set properties in the view model. These properties are "bound" to the controls, so they're updated automatically. This makes interacting with views much easier and allows you to unit test your logic via view models.

DVH Summary Script

The sample script is a simple WPF/MVVM binary plug-in that displays some DVH metrics for all the structures of the current plan. The application could not be a single-file plug-in because multiple files are needed (XAML, view models). This is a simple application, so not all patterns of MVVM are used. It is available on GitHub as DvhSummary.

In order to test this script without having to constantly open Eclipse to do so, it uses the EclipsePlugInRunner library that I wrote (see Run and Test Plug-In Scripts from Visual Studio).

In the DvhSummary.Script project, the Script.cs class is the starting point. The Execute method, which is called by Eclipse when the script is started there, calls the Run method, which is called by the EclipsePlugInRunner when tested in Visual Studio. The Run method contains the following:

var mainViewModel = new MainViewModel();
mainViewModel.PlanSetup = planSetup;

Mouse.OverrideCursor = Cursors.Wait;
Mouse.OverrideCursor = null;

var mainView = new MainView();
mainView.ViewModel = mainViewModel;

mainWindow.Content = mainView;

First, the the main view model is created, its PlanSetup property is initialized, and the DVH metrics are calculated. Then, the main view is created (a UserControl) and its ViewModel property assigned to the main view model. If you look at MainView.xaml.cs, when the ViewModel property is set, the view's DataContext is set to the view model. This tells the view that any data bindings are relative to the view model. Finally, the main window's content is set to the main view, which is necessary in order to display it.

The MainViewModel class contains the PlanSetup property, which specifies which plan to use to calculate the metrics, and the DvhSummaries property, which is a list of DvhSummaryViewModels that is bound to the DataGrid. The CalculateDvhSummary method uses an external class to calculate the metrics and obtain the list of DvhSummaryViewModels. The DvhSummaryViewModel class contains properties for the DVH metrics of interest:

public class DvhSummaryViewModel
    public string StructureId { get; set; }
    public double Volume { get; set; }
    public DoseValue Mean { get; set; }
    public DoseValue Min { get; set; }
    public DoseValue Max { get; set; }
    public DoseValue NearMin { get; set; }
    public DoseValue NearMax { get; set; }

The MainView.xaml file specifies how the view will look. The main control there is the DataGrid, which will look like a table, including a header with column names. The key property in the DataGrid we assign is ItemsSource, which specifies the data for the rows of the DataGrid:

    ItemsSource="{Binding DvhSummaries}"

The ItemSource property is bound to the DvhSummaries property of the view model. (Recall that the DvhSummaries property is a list of DvhSummaryViewModels.) And here's where the magic happens. The DataGrid will automatically create a row for each item in the list and a column for each property in DvhSummaryViewModel. The column header names will be obtained from the property names. The cells in the DataGrid will be filled with the data stored in the view models. All this happens with a single binding command!

When you run this script using the runner, and choose a sample patient and plan, you will wait a little while the calculations occur, and then the script window will appear, showing you a table with the calculated results.

Final Thoughts

I hope this post gave you an idea of what WPF and MVVM are about. But I should emphasize that this example only touched the surface of these technologies. I haven't even discussed important MVVM concepts, such as property change notification and commands. If you want to learn more about these topics, I recommend that you look at the resources I listed above.

Related Posts

See All

ESAPI Essentials 1.1 and 2.0

A few months ago, I introduced ESAPI Essentials—a toolkit for working with ESAPI (available via NuGet). I've recently added one major feature: asynchronous access to ESAPI for binary plugin scripts. Y

Announcement: ESAPI Subreddit

A few months ago, Matt Schmidt started the ESAPI subreddit. It's another useful resource for finding and discussing ESAPI-related topics. According to the description, This community is to post and di

Dump All Patient Data from ESAPI

If, for whatever reason, you need to dump out all of the data for a patient from ESAPI, there's a quick and dirty way to do it. Well, there are probably several ways to do it, but here's one I've foun


bottom of page