I recently had to build a new UI for a website and its accompanying Windows client app. As a front-end developer used to HTML, CSS and JS, I knew the website part would be easy, but redesigning the Windows app was out of my comfort zone. Instead of choosing a web-based app framework like React Native or Electron, I opted to learn XAML and WPF. Here's why — and how I got started.
My goal: build a new UI for a Windows app
Building a Windows app UI isn't your usual job for a front-end developer! Our company predominantly works with clients using Microsoft products, so I regularly see Windows app projects. When this project came up, I decided to use it as an opportunity to branch out. The brief was to redesign the app's UI, so that we could modernize it and match it to the client's branding, so styling was the big priority.
I would mainly be focusing on the presentation layer, in other words the "front-end" of the app. The release configuration, database management, as well as a lot of the ground work to retrieve the necessary data, would be handled by my colleague. Of course, working on the frontend would be interlinked with the back-end. To that end, I was keen to clean up some legacy spaghetti code in the back-end. I also wanted to get rid of legacy libraries and technologies.
The previous version of the app was coded in C# and WinForms. WinForms is one of the oldest GUI coding frameworks. Needlessly to say, it had very limited styling capabilities, so I couldn't use it for the new UI. It was clear that I would need to migrate to something more flexible.
Why I decided against Electron and React Native
My first inclination was to try and rebuild the app with a web-based app framework like React Native or Electron. In theory, these frameworks would allow a frontend developer like me to build "native" apps with only HTML, CSS and JS — my comfort zone.
Unfortunately, these frameworks didn't fit the bill for my requirements. Specifically:
- Electron and React Native use NodeJS for their apps' backend functionality. That means I would've had to recode the previous Windows app's functionality for Node, which would've taken a lot of effort.
- The app required Windows system access rights: it needed to be able to access Microsoft Services data, system configs, and privileged files. It would be hard to obtain these access rights with an Electron or React Native app, because these frameworks operate at too high a level of abstraction. I could have written an API to do these things for me, but that would have taken more than half a year's worth of work — total overkill for a UI reskin.
- One of the key arguments for using Electron and React Native is that they make cross-platform compatibility easier. This didn't apply in my case, though, because our app only needed to work on Windows.
- App performance with frameworks like Electron or React Native would be slower than with a native app.
For these reasons, I realised that I'd have to use the existing app as a base and learn how to do a UI refresh in the world of C#, .NET, WPF and XAML!
Starting out with XAML and WPF
The challenges — and how I overcame them
1. The learning curve with C# and XAML and the lack of community support
This was one of my first projects in C# and XAML, so I faced a steep learning curve trying to learn both of these languages and syntaxes at the same time. Furthermore, as front-end web developers, we're very spoiled when it comes to documentation and community support. There's almost always a neat question and answer on StackOverflow. With C# and XAML, though, I really struggled to find good community solutions to the problems I ran into. This was, of course, partly because I didn't know the XAML lingo and therefore didn't always search using the proper keywords. I was, in effect, asking the wrong questions. As time went on, I slowly started spotting the right keywords in my reading. I also tapped into the ZATech community to get some expert input; more on this later.
2. Getting too stuck in a front-end developer mindset
Just because XAML doesn't do something the way HTML does it, doesn't mean it's wrong or bad. HTML is just the "evil" we're used to. It was only once I started taking off my front-end hat to judge XAML on its own strengths, that I really started to enjoy working with it. There were two areas specifically where my front-end developer mindset was holding me back.
Understanding the relationship between content, functionality, and styling: As I mentioned above, when I started out I did what every foolish front-end developer would have done: I tried looking for the equivalents of HTML and CSS functionality in XAML. This quickly became a source of frustration, because it turned out that XAML doesn't do markup and styling in remotely the same way as HTML and CSS. XAML treats layout as part of the markup content whereas HTML and CSS treat it as part of the styling. With XAML, you do the content, some of the functionality and the layout in the C# markup, and do only styling in the style section.
TIP: Don't see layout as styling, see it as part of the content. I found it best to do layout first, program functionality second, and then do styling last.
Getting used to XAML's strictness with controls: XAML is very strict with what "controls", for example a panel, you have to use for what. It's a so-called "type and structure-heavy" markup language. Every control has a unique purpose and function: it has its own class, with its own set of rules, events and functions. Styling properties are also specific to each control. This means you need to be very specific with how you use and style a control. By contrast, with HTML and the right styling, you could theoretically use any tag, for example a <div>, for anything. The only thing holding you back would be complaints from Google's semantic web crawlers.
Later down the road, the biggest challenge turned out to be figuring out XAML's styling. The problem wasn't doing layout — XAML's layout tools are amazing. Remember when CSS incorporated Flexbox and gave us world peace and solved all our other problems? Well, XAML's layout has been doing it that way for quite a while now! Unfortunately, the rest of the styling is very cumbersome, especially when you're trying to match it to your brand.
For example, in XAML, you can use the Resources control to apply universal styling to all your controls of a certain type. So, in theory, if I wanted to make all my borders black, all I had to do is set the strokecolor for the border control to black. Seems simple enough, right? Wrong! What I didn't know was that most controls are built out of other, more abstract controls. Almost all of these controls have a border element in them, but it isn't normally visible — they're just used for padding. Because I set a universal color now, I could suddenly see all the invisible borders, which made my app look like a checkerboard!
"Fair enough -.-" I thought, so I turned that off, and decided to add border styling to each individual control I was using. Then, I found out that even though a control has a border, setting the border inside the control isn't possible — at least not without diving into 'templating'. Templating meant customizing how controls were drawn; that really requires you to have done your homework!
This broke my front-end developer brain. I longed for HTML and CSS, where setting a border and padding on virtually every element couldn't be simpler. I eventually got over this by diving into templating. I had to go to all that effort, just to change the border color!
I won't even dive into how much trouble I had to go through just to give buttons round edges! TL:DR, I gave up.
Control-specific events: Once I got used to it, I really enjoyed working with a more structured and type-based markup language: every control had a specific purpose and function, and that works well when interacting with it via C#. Each control has events that are appropriate to it. For example, the
ComboBox control has a
onIndexChange event, which you can then use in your C# function to receive parameters for the old and new values and indexes. By comparison, in HTML,
select only has an
onchange event, which is generic to all elements. In this way, working with a type-based markup language makes you feel like a "real" developer! :P
UserControls are basically abstracted components which you can code and style separately and then import as a control. Essentially, they're like styled components in front-end terms. I found them useful because much of the time you might have to code your own controls, like time pickers, loading bars, or gauges. It's convenient that there is already a baked-in system in place to support that.
Binding: You can bind the value of a variable in the C# back-end code to the value of a UI element, such as a textbox. That way, if the textbox's input changes, you can directly work with the value in the C# code. This makes the form-code interaction very smooth. It became especially useful when I was working with tables (
GridView), because I didn't have to do constant lookups.
C# as a language: I'm not picky with my coding languages, as long as they have good documentation and support. With extensive libraries and Windows' recently-improved documentation, C# has both. Unfortunately, as I mentioned, the same wasn't quite true for XAML.
In the end, building a native Windows app UI was actually quite alright! I still prefer to stick to HTML, CSS and JS whenever I can, but it was definitely an interesting experience getting to know XAML and WPF. My most important takeaway was the value of keeping an open mind and not getting too stuck in a front-end developer mindset.
I could've worked through a textbook or done a course online, but I decided to figure things out as I went along. Going over documentation and YouTube videos only gave me programming knowledge, but what I really needed, was programming wisdom. Therefore, one of the most useful resources turned out to be the ZATech community. I would recommend asking questions there to anyone starting out.
Louw is a front-end web developer aspiring to become a full stack developer. He works at GTConsult.