WPF Best Practices 2021
From healthcare to accounting to education, nearly every organization has its own website by now. The ways we learn, work, and share are so deeply immersed in websites that you might think web development has completely overtaken desktop development.
But, even with how popular websites have become, developers still create thousands of desktop applications each year. This demonstrates that the industry of desktop development is still alive and well. Many professional organizations, multi-national companies, even regular users, still rely on desktop software to get things done.
Developers have traditionally created Windows applications using Windows Forms (WinForms). But WinForms has limitations when you’re building sophisticated modern applications. The Windows Presentation Foundation (WPF) intends to overcome these hurdles and provide additional functionality.
With more than 15 years in the desktop development industry, WPF has advanced and progressed — and so have their best practices. To help you keep up with WPF and learn more about where it’s at now, this article will examine several best practices for WPF developers that will help you now, in 2021, and beyond.
The Best Practices
This section discusses several critical best practices that desktop developers like you can implement when working with WPF.
Move to .NET 5+
Microsoft .NET 5+ is the official way to move forward, as Microsoft .NET only supports .NET 5+. It offers flexible deployment, robust performance, and a modern way of building desktop applications. .NET 5+ offers almost two times more performance than the older .NET framework.
Also, with the introduction of .NET 6, it’s possible to leverage C# 10 to write more efficient and concise code. WPF applications built on .NET 5+ can also run natively on Windows on Arm devices. This partially enables you to write once and deploy anywhere when you’re developing WPF applications.
If your application isn’t currently on .NET 5, it’s easy to upgrade a WPF app with the upgrade assistant. This update offers enhanced performance and efficiency advantages.
Embrace Async and Await
Async and Await are in the Task Parallel Library and work well with the WPF. If you aren’t fond of older functions, like Run, ContinueWith, or Invoke, you’ll likely prefer the Async and Await functions. They make life a lot simpler because they’re simpler to understand.
When you Await the task, the following lines of code won’t execute until completion of the current job. With WPF, it’s even possible to have Async method handlers for WPF events, like button clicks.
Let’s understand how you can easily use Async and Await to replace the older functions. The code below is the old way of suspending a thread until a task is completed:
private void buttonClick(object s, RoutedEventArgs e)
{
button.IsEnabled = false;
button.Text = "Starting task"; var task = Task.Run(() =>
{
Thread.Sleep(1000<a name="_Int_Pskp4/89">); /</a>/ making task sleep for 1 Second
});
task.ContinueWith((t) => // executed after 1 Second of sleep
{
Dispatcher.Invoke(() => // Dispatcher consist queued items
{
button.IsEnabled = true;
button.Text = "Completed task";
});
});
}
Now, compare the following code, where Async and Await are used to simplify the code and made it more readable:
private async void buttonClick(object s, RoutedEventArgs e)
{
button.IsEnabled = false;
button.Text = "Starting task";
var result = await Task.Run(() => // awaiting the task
{
Thread.Sleep(1000); e
return "Completed long task";
});
button.IsEnabled = true;
button.Text = result;
}
As you can see, the first step requires that you put the async keyword right after the access modifier in the function. It throws an error if you don’t use async, as the async function is the only one that can call await.
Then, you await the task you want to perform. The code entered after await executes automatically once the awaited job is complete.
Another convenient feature is the ability to await multiple tasks and then execute the following statement. You can achieve that by using WhenAll.
...Functions
var results = await Task.WhenAll(buttonClick, eventClick);
...Next Statements
While using await and async, you need to be careful not to block the UI thread. You can do that safely by handling UI and Method in a different thread instead of keeping in the same one.
The example code below demonstrates this.
Simple Async and Await Functions
private async void Button_Click_3(object s, RoutedEventArgs e)
{
var returnValue = await Task.Run(()=>
functionToBeAwaited(this, intParam1, intParam2, intParam3));
}
Async Function
internal void functionToBeAwaited (MainWindow gui, int param1, int param2, int param3)
{
gui.UpdateWindow("Work Started");
while (true)
{
gui.UpdateWindow(".");
System.Threading.Thread.Sleep(50);
}
gui.UpdateWindow("Done and Done");
}
Leverage Smarter Intellisense
The latest releases of Visual Studio come with significant improvements by the WPF. By leveraging these improvements, we can write code faster and wiser.
One notable improvement is suggestions for the most valuable item after typing the initial characters of an item. For example, if we type “Grid,” Intellisense puts ColumnDefinitions and RowDefinitions on the top of a list of suggestions.
Then, if we type “ColumnDefinitions” or “RowDefinitions” and add a space, Intellisense tells us whether we must use Width or Height. These kinds of small details can help us write code faster.
Design Time Binding
Design Time Binding is another feature introduced in .NET 5. Design Time Binding allows you to bind sample data to our form controls, giving you a sense of layout while creating your application. You don’t need to run the application — just type in data and change the width of TextInput.
Also, remember that you’re building a large form and that you’ll be making changes. Your work would be tedious and time-consuming if you had to type in the data all the time. With Design Time Binding, you can avoid this repetitive kind of work.
The image above has two textboxes containing the names Tim and Corey. With the help of Design Time Binding, we can bind template values to control Design Time Bindings by declaring values using d:type.
For example, we can see that Corey doesn’t fit perfectly in the text box. Using Design Time Bindings, we can simply increase the value of the width variable. Also, Design Time Binding isn’t just limited to text. We can use a checkbox, radio buttons, images, and many other things. Refer to the official Microsoft documentation to learn more about it.
Hot Reload and Live Visual Tree
Hot Reload is a widely known feature that’s supported by most languages. It updates the layout or functionality according to the code you’ve changed without restarting the entire application. So, as soon as you save the code, your changes take place. You can enable this feature in WPF by navigating to Tools, then Debugging, and then selecting Hot Reload.
Another feature you can enable is in the WPF and UWP set of options. By selecting Preview selected elements in Live Visual Tree while debugging, you can make the debugging process faster by making navigation between XAML elements easier. Also, it gives you the ability to select elements on the UI screen, like a web development inspector.
Don’t Reinvent the Wheel
Not reinventing something is by far one of the best developer practices you can implement. If you’re making something huge, and you need a component that WPF doesn’t include an out-of-the-box solution, building that from scratch is a bad idea.
There are two reasons for this: First, why would you want to create something that’s been already built and maintained by someone else? Investing time into that isn’t productive — and surely it isn’t enjoyable. Second, you can use the time you saved elsewhere by leveraging many open-source WPF components that are already made.
Open-source components are usually well-made and are maintained by the open-source community. This means these components might already follow the best practices related to WPF and XAML. So, even if you don’t find exactly what you want, extracting a similar open-source component and editing to suit your needs is possible.
Another compelling argument for a managed components toolkit is that it saves time in development hours — not just for one project, but for multiple projects. Finally, there’s a significant advantage in the periodic implementation of new components and enhancements of existing components.
Conclusion
As you can see, desktop development isn’t dead. This article began by emphasizing the importance of using .NET 5/5+ and discussed how WPF applications that run natively on Windows on Arm devices help you write once and deploy anywhere.
The Async and Await functions simplify code in complex situations and that you can also use them for multiple awaited calls inside the Async function.
Leverage .NET 5 XAML IntelliSense to write code faster. Design Time Binding gives you a sense of the interface. Hot-Reload and Live Visual Tree are the most remarkable features for productivity and ease of development.
Originally published at https://www.developer.mesicus.com on November 30, 2021.