GUI Design – Systematic Strategy for Designing and Implementing GUIs

cguivisual studio

I am using Visual Studio to create a GUI application in C#. The Toolbox serves as a nifty component palette that allows me to easily drag and drop buttons and other elements (for clarity I'll say button whenever I mean "control") onto my form, which makes static forms quite easy to do. However, I run into two problems:

  • Creating the buttons in the first place is a lot of work. When I have a form that is not static (ie. buttons or other controls are created at run time according to what the user does) I cannot use the palette at all. Instead I have to create each button manually, by calling the constructor in whatever method I am using, and then manually initialize by specifying button height, width, position, label, event handler and so on. This is extremely tedious because I have to guess at all these cosmetic parameters without being able to see what the form will look like, and it also generates many lines of repetitive code for each button.
  • Making the buttons do something is also a lot of work. Dealing with events in a full-featured application is a huge pain. The only way I know how to do this is to select a button, go to the events tab in its properties, click the OnClick event so that it generates the event in the Form's code, then fill in the body of the event. Since I want to separate logic and presentation, all of my event handlers end up being single-line calls to the appropriate business logic function. But using this for many buttons (for instance, imagine the number of buttons present in an application like MS Word) pollutes the code of my Form with dozens of boilerplate event handler methods and it's difficult to maintain this.

Because of these, any GUI program more complicated than Hello World is very impractical to actually make for me. To be clear, I have no problems whatsoever dealing with complexity in programs that I write which have minimal UI – I feel like I am able to use OOP with a fair degree of competence to neatly structure my business logic code. But when developing the GUI, I'm stuck. It seems so tedious that I feel like I'm reinventing the wheel, and there's a book somewhere out there explaining how to do GUI properly that I haven't read.

Am I missing something? Or do all C# developers just accept the endless lists of repetitive event handlers and button creation code?


As a (hopefully helpful) hint, I expect that a good answer will talk about:

  • Using OOP techniques (such as the factory pattern) to simplify repeated button creation
  • Combining many event handlers into a single method that checks Sender to figure out which button called it, and behaves accordingly
  • XAML and using WPF instead of Windows Forms

You don't have to mention any of these, of course. It's just my best guess as to what sort of answer I'm looking for.

Best Answer

There are several methodologies that have evolved over the years to deal with these issues you've mentioned, which are, I agree, the two main issues that UI frameworks have had to address in recent years. Coming from a WPF background, these are approached as follows:

Declarative design, rather than imperative

When you describe painstakingly writing code to instantiate controls and set their properties, you're describing the imperative model of UI design. Even with the WinForms designer, you're just using a wrapper over that model - open the Form1.Designer.cs file and you see all that code sitting there.

With WPF and XAML - and similar models in other frameworks, of course, from HTML onwards - you're expected to describe your layout, and let the framework do the heavy lifting of implementing it. You use smarter controls such as Panels (Grid, or WrapPanel, etc) to describe the relationship between UI elements, but the expectation is that you don't manually position them. Flexible concepts such as repeatable controls - like ASP.NET's Repeater or WPF's ItemsControl - help you create dynamically scaling UI without writing repeating code, allowing dynamically growing data entities to be represented dynamically as controls.

WPF's DataTemplates allow you to define - again, declaratively - little nuggets of UI goodness and match them to your data; for instance, a list of Customer data objects might be bound to an ItemsControl, and a different data template invoked depending on whether he's a regular employee (use a standard grid row template with name and address) or a Prime Customer, whereas a different template, with a picture and easy-to-access buttons are displayed. Again, without writing code in the specific window, just smarter controls that are aware of their data-context, and thus allow you to simply bind them to your data and let them perform relevant operations.

Data Binding and Command Separation

Touching on data binding, WPF's databinding (and, again, in other frameworks as well, like AngularJS) allows you to state your intent by linking a control (say, a textbox) with a data entity (say, the Customer's Name) and let the framework handle the plumbing. Logic is handled similarly. Instead of manually wiring up code-behind event-handlers to Controller-based business-logic, you use the Data Binding mechanism to link a controller's behavior (say, a button's Command property) to a Command object which represents the nugget of activity.

It allows this Command to be shared between windows without rewriting the event handlers every time.

Higher Level of Abstraction

Both of these solutions to your two problems represent a move to a higher level of abstraction than the event-driven paradigm of Windows Forms that you rightfully find tiresome.

The idea is that you don't want to define, in code, every single property that the control has, and every single behavior starting from the button click and onwards. You want your controls and underlying framework to do more work for you and allow you to think in more abstract concepts of Data Binding (which exists in WinForms, but is nowhere near as useful as in WPF) and the Command pattern to define links between UI and behavior that don't require getting down to the metal.

The MVVM pattern is Microsoft's approach to this paradigm. I suggest reading up on that.

This is a rough example of how this model would look and how it would save you time and lines of code. This won't compile, but it's pseudo-WPF. :)

Somewhere in your application resources, you define Data Templates:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

Now, you link your main screen to a ViewModel, a class which exposes your data. Say, a collection of Customers (simply a List<Customer>) and a Command object (again, a simple public property of type ICommand). This link allows binding:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

and the UI:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

And that's it. The ListBox's syntax will grab the list of Customers off of the ViewModel and attempt to render them to the UI. Because of the two DataTemplates we defined earlier, it will import the relevant template (based on the DataType, assuming PrimeCustomer inherits from Customer) and put it as the contents of the ListBox. No looping, no dynamic generation of controls via code.

The Button, similarly, has preexisting syntax to link its behavior to an ICommand implementation, which presumably knows to update the Customers property - prompting the data-binding framework to update the UI again, automatically.

I've taken some shortcuts here, of course, but this is the gist of it.

Related Topic