Front-End Web Development in Desktop Applications — a hands-on project with Blazor and .NET 6
As I progress in my knowledge of software development, I can’t help but sigh at the many libraries, frameworks and languages any developer worth its salt ought to get familiar with. I don’t have an issue with it per say, I have recently graduated from college and so I was in the business of learning new and complicated things every semester. What I truly worry about is the inevitable shallowness of the knowledge acquired.
I believe most developers out there are not afraid to put in the hours in learning new technologies. However, wouldn’t it be great if you deepen your knowledge in an area instead of learning an entire different creature every time? There is great joy in seeing your knowledge growing to new heights as time goes by.
My first GUIs were made using the Qt Framework in C++/Python and now I am transitioning into the technologies used in Web Development (i.e. HTML, CSS, JS). I won’t bore you with the reasons behind this decision, let’s just say horizons are greatly widened this way.
When Microsoft released its third preview of the .NET 6 with its brand-new Blazor WebView control for WinForms, I received the news with great enthusiasm as I could see a platform for the deep growth of knowledge in web front-end technologies. In other words, I could develop for Web and Desktop using very similar technologies rather than two entire beasts. Sure there is Electron, but that has never attracted me.
The Coreflood Project — the Back-End
The Coreflood project begun as a college assignment during my studies of Reservoir Simulation. It seeks to solve the following problem:
“To find the pressure and saturation fields during drainage and imbibition of a water-wet core sample (rock) using the two-phase IMPES numerical simulation.”
What does the code for that look like? Well, without the GUI, the Coreflood project is just a number-crunching OOP console application that, out of necessity, has multithreading support in order to quickly achieve a solution. So in short, it takes a bunch of parameters, does some heavy and numerous mathematical calculations (i.e. solution of thousands of linear systems) and gives me the pressure and saturation values.
Recently I ported the code to C# (it was ported from C++), and now with the launch of the .NET 6 and the Blazor WebView control for WinForms, it seems as good as any of an opportunity to give this project a proper GUI with the most recent frameworks such as Bootstrap and Blazor.
The Coreflood Project — the Front-End
What does my GUI needed to do? It needed to collect the many required parameters for the simulation and display the results in charts. Just that! Simple and direct.
Let’s see some code!
I started the project with a simple WinForms template in Visual Studio and with the WebView control installed, I just dragged and dropped the control into my form and that’s all I needed to do within the Form Designer.
Consider the following snippet:
We need first to inject some dependencies. This is part of the mandatory initial setup. From line 24 to line 29, I was setting up Blazorise, then I added MatBlazor and lastly the WebView service.
Line 36 to 45, is mostly boilerplate code. However, special attention must be given to line 43. Just like in React, where you render your components to an element in the HTML file that matches the provided id, the same behavior repeats with Blazor.
Component Hierarchy and Pattern of Communication
The Index component is the parent component of the entire application and is composed of smaller child components. Its child components are the four charts used to display the results and the two forms needed to collect the input data.
With this structure in mind, one important issue arises: how to establish communication between components? I won’t elaborate on it when there is an excellent article from Mr. Abrickis detailing the whole dynamic. Check it out!
You can observe this pattern of communication applied in the snippet below where the EventCallback is passed through the FormComplete attribute.
Since there are many parameters in my simulator, I chose to pack them in a class (it was a struct, initially), forming a Data Transfer Object which I added support for Data Validation, since all values are required and some of them must be within a certain range.
With my DTOs I can now attach them to a Form and collect the required user input. You can see below what the form looks like:
To enable data validation for the form, you need the tag at line 9 and if there is an error message associated with a property, you need the code in lines 17 and 31.
Firing up the Back-End
One of my great concerns in developing applications has to do with performance. How smooth does it run? Does it freeze every now and then? What about memory consumption?
When it comes to GUI applications, my main concern is when coupling the front-end with heavy calculations from the back-end. If you do it all in one thread, when you start your back-end routines, the GUI will lock and freeze until the back-end finishes its task. Thus, it is imperative that both be independent, lest the GUI be rendered unable to respond to user events.
Asynchronous Programming is a great tool to achieve this.
When this button is pressed…
This function is called:
The async “magic” happens between line 6 and 11. In short, every instruction needed to start the simulation was inserted in a lambda function, which in turn was passed to a Task to run asynchronously. Bear in mind that none of the statements within the lambda block are asynchronous. And just like that, my GUI will still respond to events while the back-end is solving thousands of linear systems! Great!
If you don’t quite grasp the idea behind this implementation, please refer to an excellent video by Mr. Tim Corey in which he explains with enough detail the basic workings of async code.
Chart Creation and Performance
My second biggest performance concern was with drawing the charts in the GUI. There are four scatter plots needed to display the simulation results. Each of them has a slider that when dragged, changes the data used in the plot, effectively redrawing the chart. Thus, it follows that redrawing a chart with 100+ points could become a bit slow, jittery, downright unsmooth.
When the simulation is done, this following function is called:
As soon as the PlotData DTO is filled, the ChangeDataset function from each of the chart components is called. Beware, to properly call the function of the chart, first associate your Razor component to a class field with the @ref tag like in the code snippet below.
Once the ChangeDataset function is executed, the chart is drawn with the simulation data. With up to 100 points, I felt all four charts to be very smooth when making transitions with the slider, which was very surprising to me, and most certainly welcome. Though I admit, I haven’t measured the performance in a limited hardware.
Writing Literal HTML
One thing I wanted to do, out of curiosity, was to see if I could prepare a string with HTML content, insert it in the body of my component and then render what I need rendered. This is something I’d do in very specific situations, rather than it being a coding habit. However, since I’m stretching out to see what I’m capable of doing with this technology, I’ve coded my Progress Bar using this concept.
There is a class that will perform this task for us: MarkupString. Next, I needed to prepare my strings with the HTML content. The contents I needed prepared were elements from the progress bar in order to display how my simulation was progressing. Once I was done with it, I wrapped the results in a static class.
The four elements needed for my progress bar are written, in Razor syntax, as follows:
Each of these elements, when the App is initialized are set to a MarkupString containing no HTML content, that is, an empty string. Thus, progress is set to zero. As the simulation progresses, the content is assigned like in line 4 in the snippet below. It is not needed to call any additional function to re-render or anything like it.
If you wish to understand the inner workings of this progress bar, refer to Bootstrap’s documentation.
Performance is great, both in the front-end and in the back-end. It is better than I expected. Memory Consumption is a bit tall, reaching 300–400 MB for a 100 blocks simulation but this is due to the current implementation of the back-end. In the future, I aim to change to be just a fraction of it. It’s an easy fix, but painful to implement.
This article is not meant to teach you the technologies herein discussed. Rather, I assume you have some familiarity with the concepts involved and wanted to show you some of the things we can do when developing desktop applications with web technologies. It was a fun project that I am very satisfied with its outcome.
If you, dear reader, have any suggestions, you are more than welcome to bring them forth. I am not a coding guru, I can always learn a thing or two. You can check the entire project in the project’s repository in my GitHub.