How to Add WPF UI Controls to Your Desktop Application

MESCIUS inc.
MESCIUS inc.
Published in
9 min readMar 25, 2022

--

Windows Presentation Foundation (WPF) is a user interface framework for creating desktop applications. It has become the standard for developing Windows application user interfaces, as it provides the user with the ability to define views using the Extensible Application Markup Language ( XAML). The inclusion of WPF in .NET Core by Microsoft has made WPF a more promising option for developing future Desktop applications.

This blog post will guide you on creating a new Windows Presentation Foundation (WPF) application with Visual Studio 2022 and .NET 6 framework. After configuring the basic application, you will learn how to add the default controls available in the toolbox, then add third-party controls available in the toolbox by installing the appropriate NuGet packages.

So, let’s get started with creating a WPF application.

Creating a WPF Application

The steps described in this section will require Visual Studio 2022, as we will be creating the application using .NET 6 framework. So, make sure you are working with Visual Studio 2022 to follow the steps smoothly.

  1. Open Visual Studio and select Create a new project. Type ‘WPF’ in the search box to minimize the displayed project templates. The screenshot below shows that the WPF search lists the WPF application templates for C# and VB languages. We will be choosing the C# variant to create the WPF application.

2. The project creation is followed by project configuration providing the basic details like project name and location. In the final step of project configuration, the below screen is displayed to choose the desired .NET framework. Here, we choose .NET 6 framework to create the application.

3. Once the project is created and configured, it loads the MainWindow.xaml file as depicted below. This file is the design form for the WPF Window MainWindow added by default to the newly created project. It is used to design the Window by defining the Window layout and adding controls as described in the steps ahead.

Adding WPF Controls from Toolbox

In this section, we will learn how to add the default Microsoft .NET WPF controls available in the toolbox to the WPF application. The screenshot below depicts some of the default controls available in the toolbox under All WPF Controls tab.

Design Window Layout

Before adding controls to the WPF Window, we must understand the various Layout controls available in WPF, which help with place and size controls. The default layout control provided in XAML is the <Grid> control. The Grid control lets you define rows and columns, like a table, and place controls within the bounds of a specific row and column combination.

You can have any number of child controls or other layout controls added to the Grid. A grid always has a single row and column declared. For details on other layout controls, refer to the following link.

For this blog, we will be working with the default Grid control with two rows. One row will display the control name we will be adding to the application, and the other row will contain the actual control. The described window layout can be configured by adding the following markup to XAML:

<Window x:Class="UIControlsDemo.MainWindow"  
xmlns="[http://schemas.microsoft.com/winfx/2006/xaml/presentation](http://schemas.microsoft.com/winfx/2006/xaml/presentation)"
xmlns:x="[http://schemas.microsoft.com/winfx/2006/xaml](http://schemas.microsoft.com/winfx/2006/xaml)"
xmlns:d="[http://schemas.microsoft.com/expression/blend/2008](http://schemas.microsoft.com/expression/blend/2008)"
xmlns:mc="[http://schemas.openxmlformats.org/markup-compatibility/2006](http://schemas.openxmlformats.org/markup-compatibility/2006)"
xmlns:local="clr-namespace:UIControlsDemo"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="800" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="400"></RowDefinition>
</Grid.RowDefinitions>
</Grid>
</Window>

The screenshot below depicts the Window layout generated by adding the above defined XAML markup:

Add Controls from Toolbox

Now, let’s quickly understand how to add controls to the Window layout. Adding controls to a WPF Window simply involves selecting the desired control from the toolbox and drag-drop it onto the expected location on the Window.

We will begin by adding the Label control in the first row of the Grid. As depicted below, the control has been selected, dragged from the toolbox, and dropped in the first row of the Grid. After the control has been dropped, it displays a list of basic properties to configure the control:

Once the Label control has been added, it is configured by setting the following properties in markup:

<Label Grid.Row="0" Content="MS Grid" FontSize="24" FontFamily="Arial" VerticalAlignment="Center" HorizontalAlignment="Center"></Label>

The screenshot below depicts the configured Label control:

Next, we add the DataGrid control in the second row of the Grid. By default, DataGrid control is added with a sample data source set using the ItemsSource property of DataGrid. We will replace the default code with the following code to configure DataGrid control:

<DataGrid Grid.Row="1" x:Name="MSGrid" Height="300px" Width="600"/>

This is how the Window layout looks after adding and configuring the DataGrid control:

Binding DataGrid to Data

Let’s just quickly bind the DataGrid control to a valid DataSource to understand the default look and feel of the control.

Add Customer Class

Add a new class named Customer by adding the following code, which will be used to generate a datasource:

internal class Customer  
{
//fields
int _id, _countryID;
string _first, _last;
double _weight;
bool _active;

//data generators
static Random _rnd = new Random();
static string[] _firstNames = "Andy|Ben|Charlie|Dan|Ed|Fred|Gil|Herb|Jim|Elena|Stefan|Alaric|Gina".Split('|');
static string[] _lastNames = "Ambers|Bishop|Cole|Danson|Evers|Frommer|Salvatore|Spencer|Saltzman|Rodriguez".Split('|');
static string[] _countries = "China|India|United States|Japan|Myanmar".Split('|');

public Customer()
: this(_rnd.Next())
{
}
public Customer(int id)
{
ID = id;
Active = false;
FirstName = GetString(_firstNames);
LastName = GetString(_lastNames);
CountryID = _rnd.Next() % _countries.Length;
Weight = 50 + _rnd.NextDouble() * 50;
}
//Object model
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
RaisePropertyChanged("ID");
}
}
}
public bool Active
{
get { return _active; }
set
{
_active = value;
RaisePropertyChanged("Active");
}
}
public string Name
{
get { return string.Format("{0} {1}", FirstName, LastName); }
}
public string Country
{
get { return _countries[_countryID]; }
}
public int CountryID
{
get { return _countryID; }
set
{
if (value != _countryID && value > -1 && value < _countries.Length)
{
_countryID = value;
RaisePropertyChanged(null);
}
}
}
public string FirstName
{
get { return _first; }
set
{
if (value != _first)
{
_first = value;
RaisePropertyChanged(null);
}
}
}
public string LastName
{
get { return _last; }
set
{
if (value != _last)
{
_last = value;
RaisePropertyChanged(null);
}
}
}
public double Weight
{
get { return _weight; }
set
{
if (value != _weight)
{
_weight = value;
RaisePropertyChanged("Weight");
}
}
}
// utilities
static string GetString(string[] arr)
{
return arr[_rnd.Next(arr.Length)];
}
static string GetName()
{
return string.Format("{0} {1}", GetString(_firstNames), GetString(_lastNames));
}
// static list provider
public static ObservableCollection<Customer> GetCustomerList(int count)
{
var list = new ObservableCollection<Customer>();
for (int i = 0; i < count; i++)
{
list.Add(new Customer(i));
}
return list;
}

// this interface allows bounds controls to react to changes in the data objects.
void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}

Bind data to DataGrid

The code below generates the datasource using the Customer class and binds it to the DataGrid control using the ItemsSource property:

private void Window_Loaded(object sender, RoutedEventArgs e)  
{
//Generate grid data
var gridData = Customer.GetCustomerList(12);
//Binding the grid with data
MSGrid.ItemsSource = gridData;
}

The screenshot below depicts the DataGrid control after binding it to the Customer datasource:

Adding WPF Controls from NuGet

After understanding the working with default controls, we will now see how we can work with advanced controls provided by third-party vendors, which might be required if we want to create a feature-rich desktop application with complex requirements.

To work with the third-party controls, obviously, we need to have these controls added to the toolbox as the foremost step. This can be accomplished by accessing the NuGet Package Manage for an application via the Tools option in the Menu Bar. From the NuGet Package Manager menu, choose Manage NuGet Packages for Solution, which opens the Manage Packages window as depicted in the screenshot below.

To load the specific packages, you will need to type in the package name in the Search panel found under the Browse tab. Selecting the appropriate package in the left panel, you can install it in the project using the Install option available in the right panel of the window.

In this blog, we will be working with ComponentOneWPF controls, and in comparison to the DataGrid control, we will showcase FlexGrid control from the ComponentOne WPF Edition. Hence, the screenshot below depicts the installation of C1.WPF.Grid Nuget package.

As soon as the C1.WPF.Grid package is installed, the following controls start appearing in the Toolbox under the C1 WPF Controls tab. It consists of many controls, including the FlexGrid control, as the FlexGrid control has several features dependent on the other controls in the suite.

Once the controls have been added to the toolbox, they can be accessed and similarly added to the Window form as we were adding the default controls, i.e. using the drag and drop from toolbox.

Working with ComponentOne FlexGrid

This section briefly describes how to work with the FlexGrid control in a WPF application. For details, you may refer to FlexGrid Quickstart.

To begin with, we add a new WPF Window to the project named FlexGridWindow, to showcase the FlexGrid control as depicted below:

Following the same design approach as described for MS DataGrid, we will design the Window layout and add Label and FlexGrid controls to Window using the following markup:

<Window  
xmlns="[http://schemas.microsoft.com/winfx/2006/xaml/presentation](http://schemas.microsoft.com/winfx/2006/xaml/presentation)"
xmlns:x="[http://schemas.microsoft.com/winfx/2006/xaml](http://schemas.microsoft.com/winfx/2006/xaml)"
xmlns:d="[http://schemas.microsoft.com/expression/blend/2008](http://schemas.microsoft.com/expression/blend/2008)"
xmlns:mc="[http://schemas.openxmlformats.org/markup-compatibility/2006](http://schemas.openxmlformats.org/markup-compatibility/2006)"
xmlns:local="clr-namespace:UIControlsDemo"
xmlns:c1="[http://schemas.componentone.com/winfx/2006/xaml](http://schemas.componentone.com/winfx/2006/xaml)" x:Class="UIControlsDemo.FlexGridWindow"
mc:Ignorable="d"
Title="FlexGridWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="C1 FlexGrid" FontSize="24" FontFamily="Arial" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<c1:FlexGrid x:Name="C1Flexgrid" Grid.Row="1" Grid.Column="1" Height="500px" VerticalAlignment="Center" HorizontalAlignment="Center"></c1:FlexGrid>
</Grid>
</Window>

The only difference here is the addition of a reference ‘c1’ for referring to C1 controls. This is how the window looks after adding the above markup:

In this last step, we bind the FlexGrid control to the same datasource we used for MS DataGrid binding using the ItemsSource property of FlexGrid class:

private void Window_Loaded(object sender, RoutedEventArgs e)  
{
//Generate grid data
var gridData = Customer.GetCustomerList(12);
//Binding the grid with data
C1Flexgrid.ItemsSource = gridData;
}

Now, let’s quickly update the StartupUri property in App.xaml file to load the FlexGridWindow as the default window upon project execution. Executing the project, you can observe the following display for FlexGrid control:

The image showcases FlexGrid displaying the data in a well-formatted presentation, having a more user-friendly user interface with sufficient row height, font settings, and data aligned to left or right depending on the datatype. This is the default look and feel of the control, which is quite appealing compared to the MS DataGrid view.

In addition, FlexGrid by default provides support for sorting, filtering, and Auto-sizing columns using a menu available on clicking the header, as depicted in the GIF below:

Apart from this basic look and feel, FlexGrid is feature-rich control, providing many features, including Grouping, Row Details Template, freezing, exporting, etc. You may refer to FlexGrid demos and documentation to explore all the available features.

Download the sample, implementing MS DataGrid and FlexGrid using the steps described in this blog.

Get access to the ComponentOne FlexGrid and other WPF samples by installing WPF Edition.

Originally published at https://developer.mescius.com on October 17, 2023.

--

--

MESCIUS inc.
MESCIUS inc.

We provide developers with the widest range of Microsoft Visual Studio components, IDE platform development tools, and applications.