Since the initial release of .NET framework almost 20 years ago, the importance of desktop application has significantly decreased. At that time, most new applications were first developed for Windows because it was by far the most common operating system. Today, it’s much more common to create a web application first, because it will work on all operating systems. Despite that, there’s still a place for native applications in scenarios where performance or user experience delivered by web applications, isn’t good enough.
Developing Windows applications in .NET framework
There are three main application frameworks available for developing Windows applications in .NET framework – Windows Forms, WPF and UWP.
They don’t only differ in the user interface that can be created with them, but also in the way the code is written, and how their interfaces are created. I will introduce these application frameworks one by one in the order they were released.
Windows Forms
The first version of Windows Forms was released in 2002 at the same time as .NET framework 1.0. At that time, the most popular tools for developing Windows applications were Visual Basic 6 and Borland Delphi 6.
Both followed the principles of rapid application development (RAD). To increase developer productivity, they offered graphical designers for creating user interfaces by arranging available user interface controls in the window. The code was written in an event-driven manner, i.e. developers were implementing event handlers which responded to user’s interaction with the application.
Windows Forms takes the same approach. Applications consist of multiple windows, called forms. Using the designer, the developer can place the controls on the forms and customize their appearance and behaviour by modifying their properties in the editor.
Editorial Note: If you are into Web Development, do read Developing Web Applications in .NET.
Figure 1: Windows Forms designer in Visual Studio 2019
As a result, most Windows Forms applications have a very similar appearance which is often referred to as battleship gray. The best way to avoid this is to use custom third-party controls instead of the ones included in the framework. Unfortunately, there aren’t many available as open-source or freeware. The most important commercial control vendors are DevExpress, Infragistics and Telerik.
Since the designer output is code, each form has two separate code files so that the code generated by the designer doesn’t interfere with manually written code. Partial classes are used so that the code from both files gets compiled into the same class.
– Designer-generated code isn’t meant to be modified manually which is also stated in the comments of the generated file:
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.emailAddressLabel = new System.Windows.Forms.Label();
this.emailAddressTextBox = new System.Windows.Forms.TextBox();
this.submitButton = new System.Windows.Forms.Button();
this.resetButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// emailAddressLabel
//
this.emailAddressLabel.AutoSize = true;
this.emailAddressLabel.Location = new System.Drawing.Point(13, 13);
this.emailAddressLabel.Name = "emailAddressLabel";
this.emailAddressLabel.Size = new System.Drawing.Size(75, 13);
this.emailAddressLabel.TabIndex = 0;
this.emailAddressLabel.Text = "Email address:";
//
// a lot code skipped for brevity
//
this.AcceptButton = this.submitButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.resetButton;
this.ClientSize = new System.Drawing.Size(272, 72);
this.Controls.Add(this.resetButton);
this.Controls.Add(this.submitButton);
this.Controls.Add(this.emailAddressTextBox);
this.Controls.Add(this.emailAddressLabel);
this.Name = "SubscribeForm";
this.Text = "Subscribe";
this.ResumeLayout(false);
this.PerformLayout();
}
– All the other code belonging to the form is placed in the second file and is under full control of the developer.
Each control raises different events during its lifetime in response to which the code in corresponding event handlers gets executed.
public partial class SubscribeForm : Form
{
public SubscribeForm()
{
InitializeComponent();
}
private void ResetButton_Click(object sender, EventArgs e)
{
emailAddressTextBox.Text = String.Empty;
}
private void SubmitButton_Click(object sender, EventArgs e)
{
var emailAddress = emailAddressTextBox.Text;
// submit entered email address
}
}
Having the application business logic spread across many event handlers in multiple forms can make the application difficult to maintain as it grows in size. It’s also challenging to write unit tests for it, leaving UI tests as the only option for automated testing. UI Tests are more fragile and more time consuming to create, than unit tests.
To avoid this issue, the model-view-presenter (MVP) design pattern can be used. This approach allows most of the code to be moved from the form (i.e. the view) to the presenter class which is responsible for reacting to events and updating the view. By mocking the views, presenters can be fully unit-tested.
Figure 2: Class interaction in model-view-presenter design pattern
The MVP design pattern requires additional plumbing code to be written. This could be avoided by using a library for that purpose, such as Composite UI Application Block (CAB) or MVC#. Although both are still available for download, neither is supported anymore.
All of this makes Windows Forms not very suitable for creating new applications. An exception could be where the nature of the application to be created makes the restrictions less important (e.g. it’s a small application that’s not customer-facing) and the developers are more experienced with this framework than with any of the others.
Another argument in favor of choosing Windows Forms over other frameworks can be its Mono implementation which also works on Linux and macOS. Although not developed or supported by Microsoft, it is highly compatible and can be a good approach for developing a desktop application for multiple operating systems.
Editorial Note: If you are still into Windows Forms development, these WinForm tutorials may come in handy.
Windows Presentation Foundation (WPF)
Windows Presentation Foundation (WPF) was released as a part of .NET framework 3.5 in 2007. Although there’s still a designer available for arranging the controls in a window, the created layout is not stored as code.
Figure 3: WPF Designer in Visual Studio 2019
Instead, it is saved as an XML file using a special syntax named XAML (Extensible Application Markup Language). Unlike the code for Windows Forms, this XML file can be much easier to understand and edit manually.
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0">Email address:</Label>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding EmailAddress, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button IsCancel="True" Command="{Binding ResetCommand}">Reset</Button>
<Button IsDefault="True" Command="{Binding SubmitCommand}">Submit</Button>
</StackPanel>
</StackPanel>
Also, the synchronization between the designer and the XML file is bidirectional: any changes made directly to the XML file are immediately visible in the designer. This allows for greater flexibility when editing the layout: individual changes can be made either in the designer or in the XAML markup, wherever the developer finds it easier to achieve her/his goal.
Additionally, the positioning and appearance of controls can be decoupled from control declaration:
– Instead of absolutely positioning controls in the window using offsets, it is preferred to use separate layout controls like StackPanel and Grid for that purpose:
Figure 4: Positing controls in the window with layout controls
· Styles can be used to define appearance and then applied to controls by control type or style name. This makes it easier to achieve unified appearance of all controls and to modify appearance of controls even after the windows were initially created.
<Application.Resources>
<Style TargetType="StackPanel">
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="Button">
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Width" Value="60"/>
</Style>
</Application.Resources>
All the controls in the framework are highly customizable. Therefore, WPF applications show much more visual variety than Windows Forms and their technical origin can’t be recognized as easily.
Figure 5: Screenshot of 3M ChartScriptMD WPF application designed by Next Version Systems
However, creating highly-customized visually appealing applications has a steep learning curve and requires experienced WPF developers.
To fill the space between the plain WPF applications and those polished manually to the highest extent, there are control collections available, both open-source (e.g. Modern UI for WPF, MahApps.Metro, and Material Design In XAML Toolkit) and commercial (e.g. available from DevExpress, Infragistics and Telerik).
Code is still event-driven. However, because of excellent binding support, it is much easier to decouple code from the layout. Both data properties and event handlers (in the form of commands) can be bound to controls in XAML markup.
By taking advantage of this, model-view-viewmodel (MVVM) design pattern became the standard approach to developing WPF applications soon after the framework was released. It is somewhat similar to the MVP pattern except that instead of the presenter directly interacting with the view, two-way binding is used for exchanging data and events between the view and the viewmodel.
Figure 6: Class interaction in model-view-viewmodel design pattern
To avoid some of the plumbing code, one of the many open-source MVVM frameworks can be used:
· Prism was originally developed by Microsoft’s Patterns and Practices team but was taken over by community once that team was disbanded.
· MVVM Light Toolkit was developed by Laurent Bugnion, now a Microsoft employee.
· Caliburn.Micro was developed by Rob Eisenberg whose latest project is the Aurelia JavaScript framework.
Although the frameworks take slightly different approaches, they all primarily make it easier to create commands, match viewmodels to views, and navigate between views.
The following viewmodel class uses Prism:
class MainWindowViewModel : BindableBase
{
private string emailAddress;
public string EmailAddress
{
get
{
return emailAddress;
}
set
{
SetProperty(ref emailAddress, value);
SubmitCommand.RaiseCanExecuteChanged();
}
}
public DelegateCommand ResetCommand { get; }
public DelegateCommand SubmitCommand { get; }
public MainWindowViewModel()
{
ResetCommand = new DelegateCommand(Reset);
SubmitCommand = new DelegateCommand(Submit, CanSubmit);
}
private void Reset()
{
EmailAddress = String.Empty;
}
private void Submit()
{
var emailAddress = EmailAddress;
// submit entered email address
}
private bool CanSubmit()
{
return EmailAddress?.Length > 0;
}
}
Even today, WPF is the most versatile and flexible framework for creating Windows desktop applications and as such the recommended choice for most new Windows desktop applications.
Editorial Note: If you are into WPF programming, check out these WPF tutorials.
Universal Windows Platform (UWP)
The origin of Universal Windows Platform (UWP) can be traced back to the release of Windows 8 in 2012 and the accompanying framework for development of touch-first applications, called Metro applications.
The framework evolved through the years, making it possible to target different Windows devices with the same codebase.
First, support was added for Windows Phone 8.1 applications. At that time, these applications were called Windows Store applications.
With the release of Windows 10 in 2015, the framework got its final name and eventually supported development of applications for Windows desktop, Windows Mobile (successor of Windows Phone which was in the meantime discontinued and will reach end-of-life in December 2019), Windows IoT Core, Windows Mixed Reality (called Windows Holographic when first introduced), and Xbox One.
At first glance, UWP is very similar to WPF.
User interfaces created in the designer are saved as XAML files. Good binding support lends itself well to the MVVM pattern. However, the controls are different enough from their WPF counterparts to make porting of user interfaces from one platform to the other, difficult.
From their beginnings in Metro applications, UWP controls focus on consistent recognizable design, support for different screen sizes and different input methods, including touch. In their latest incarnation, they follow the Fluent Design System which is also used in most if not all Microsoft’s applications distributed through Microsoft Store today.
Figure 7: Fluent Design System used in Windows Weather application
Also, because UWP applications are designed to be published in Microsoft Store, they run in a sandbox and don’t have direct access to all Win32 APIs. However, additional Windows 10 UWP APIs are available to them (providing access to Microsoft Store functionalities, such as live tiles, notifications, in-app purchases etc.) which were previously not available to WPF and Windows Forms applications.
Today, the differences between UWP applications and regular Windows desktop applications are much smaller than they were initially. Mostly because Windows desktop applications can now call Windows 10 UWP APIs and can also be published in the Microsoft Store when using the so-called Desktop Bridge tooling (originally named Project Centennial). They are of course still restricted to targeting Windows desktop devices only.
On the other hand, UWP applications can call some Win32 APIs (support differs between the devices) when their code is written in C++/CX (C++ component extensions).
UWP applications are your only choice if you want to target any non-desktop Windows devices. You might also prefer them over WPF for Windows desktop applications if you want to target other Windows devices with the same application or want to publish your application in Microsoft Store as long as you don’t need any Win32 APIs not available to you in UWP applications.
Editorial Note: If you are an UWP developer, check out our UWP tutorials.
.NET Core 3.0
In version 3.0, .NET Core will be expanded with support for Windows desktop applications written using Windows Forms or WPF. Unlike other types of .NET Core applications, these will not be cross-platform and will run only on Windows.
.NET Core 3.0 is planned for release in September 2019 and is only available in preview at the time of writing. With the latest preview of Visual Studio 2019 and .NET Core 3.0, new Windows Forms and WPF projects can already be created, built, and run. The biggest limitation at the moment is the fact that Windows Forms designer doesn’t yet work with .NET Core projects which makes it difficult to do any kind of serious development with .NET Core based Windows Forms applications. However, the issue should be resolved until the final release.
Both Windows Forms and WPF applications are also being extended with the ability to use selected UWP controls inside them (InkCanvas, MapControl, MediaPlayerElement, and WebView for now). This feature is named XAML Islands and is currently available in preview for .NET Core 3.0 and.NET framework 4.6.2 or newer. The final release for both platforms is planned to coincide with the final release of .NET Core 3.0 in September 2019.
When this happens, .NET Core 3.0 based WPF applications will most probably replace .NET framework based WPF applications as the recommended framework choice for most new Windows desktop applications. Since version 4.8 was the last feature release for .NET framework, using .NET Core instead of .NET framework for new applications will allow you to take advantage of the latest improvements (e.g. better performance, C# 8 support) which aren’t going to be ported back to the .NET framework.
It will probably only make sense to port existing .NET framework-based Windows Forms and WPF applications to .NET Core when they are still actively developed and would greatly benefit from .NET Core exclusive features (e.g. side-by-side installation of different .NET Core versions). Although the process of porting will likely improve until the final release, it will still probably require a non-trivial amount of work.
Conclusion:
The framework choice for desktop applications mostly depends on the devices which you want to target.
For applications targeting Windows desktop only, WPF is usually the best choice. Once the final release of .NET Core 3.0 is available in September 2019, it will make sense to develop new WPF applications in it. But until then, the .NET framework is your only option.
Since WPF applications don’t work on other Windows devices (such as IoT Core, Mixed Reality etc.), your best choice is to use UWP instead. This will restrict which Win32 APIs are available to you, which is the reason why WPF is preferred for desktop-only applications in most cases.
The only desktop framework not really recommended for writing new applications is Windows Forms. Despite that, it is still fully supported and will even be available in .NET Core 3.0 when released in September 2019. This means that there’s no need for rewriting existing Windows Forms applications in a different application framework.
This article was technically reviewed by Daniel Jimenez Garcia.
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Damir Arh has many years of experience with software development and maintenance; from complex enterprise software projects to modern consumer-oriented mobile applications. Although he has worked with a wide spectrum of different languages, his favorite language remains C#. In his drive towards better development processes, he is a proponent of Test-driven development, Continuous Integration, and Continuous Deployment. He shares his knowledge by speaking at local user groups and conferences, blogging, and writing articles. He is an awarded Microsoft MVP for .NET since 2012.