Six years of WPF; what's changed?
Prior to working full time on Octopus Deploy, I spent a year building a risk system using WPF, for traders at an investment bank. Before that I worked as a consultant and trainer, mostly with a focus on WPF. I've lived and breathed the technology for the last six years, and in this post I'm going to share some thoughts about the past and future of WPF and the XAML-ites.
Six years ago, I wrote an article about validation in WPF on Code Project. I also wrote a custom error provider that supported IDataErrorInfo, since, would you believe, WPF in version 3.0 didn't support IDataErrorInfo. Later, I worked on a bunch of open source projects around WPF like Bindable LINQ (the original Reactive Programming for WPF, back before Rx was invented) and Magellan (ASP.NET-style MVC for WPF). I was even in the MVVM-hyping, Code Project-link sharing club known as the WPF Disciples for a while.
As I look back at WPF, I see a technology that had some good fundamentals, but has been really let down by poor implementation and, more importantly, by a lack of investment. I'm glad those days are behind me.
Back in 2006, here's what the markup for a pretty basic Window looked like (taken from an app I worked on in 2006):
<Window x:Class="PaulStovell.TrialBalance.UserInterface.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="clr-namespace:PaulStovell.TrialBalance.UserInterface"
xmlns:tbp="clr-namespace:PaulStovell.TrialBalance.UserInterface.Providers"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="TrialBalance"
WindowState="Maximized"
Width="1000"
Height="700"
Icon="{StaticResource Image_ApplicationIcon}"
Background="{StaticResource Brush_DefaultWindowBackground}"
x:Name="_this"
>
I mean, look at all that ceremony! x:Class
! XML namespace imports! Why couldn't any of that stuff be declared in one place, or inferred by convention?
Fortunately, it's now 2012, and things have come a long way. Here's what that code would look like if I did it today:
<Window x:Class="PaulStovell.TrialBalance.UserInterface.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="clr-namespace:PaulStovell.TrialBalance.UserInterface"
xmlns:tbp="clr-namespace:PaulStovell.TrialBalance.UserInterface.Providers"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="TrialBalance"
WindowState="Maximized"
Width="1000"
Height="700"
Icon="{StaticResource Image_ApplicationIcon}"
Background="{StaticResource Brush_DefaultWindowBackground}"
x:Name="_this"
>
Spot the difference? Of course not, it was a trick question, nothing has changed since 2006 that would have made that less verbose.
In contrast, here's what a web page looked like in ASP.NET in 2006 (also taken from a project in 2006):
<%@ Page Language="C#" MasterPageFile="~/TrialBalance.Master" AutoEventWireup="true" EnableViewState="false" CodeBehind="Builds.aspx.cs" Inherits="PaulStovell.TrialBalance.MainWebsite.Builds" Title="Downloads - TrialBalance" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContentPlaceholder" runat="server">
<asp:PlaceHolder runat="server" Visible="false" ID="_downloadAreaPlaceholder">
<h1>Download</h1>
What would that markup look like today?
@model BuildsViewModel
@section Main {
<h1>Download</h1>
}
I originally became a WPF developer because I didn't like ASP.NET Web Forms and models like View State. But now, when I look back at the journey ASP.NET has taken, it has made huge changes. From the Web Forms model to the MVC model, from ASPX syntax to Razor, there's been some real innovation in the ASP.NET camp in that time. There have been some real innovators in the ASP.NET camp in that time.
Here's a list of things ASP.NET has done in six years that WPF hasn't:
- Created a new, human-friendly markup language (Razor). Razor makes writing markup fun. XAML has never been fun. In fact before ReSharper introduced the 'Import namespace' support for XAML, it was downright torture.
- Embraced design patterns. You can't claim MVVM here for WPF - WPF supports data binding, but the core of WPF doesn't actually contain a single feature that helps with MVVM; it's all layered on top through Blend Behaviors and third party frameworks. ASP.NET has an entire stack built on top of MVC.
- Embraced the pit of success. You can actually build a maintainable application using ASP.NET MVC using the default project template. In contrast, without third party frameworks, the default WPF project template is the path to misery.
- Embraced extensibility. Nearly everything in ASP.NET MVC has an interface or abstract class that you can extend to change how the framework works. There's a beautiful pipeline that you can plug into. I'd swear the WPF team never even heard of an interface, and the only abstract classes have
internal
constructors. - Embraced open source. ASP.NET MVC bundles jQuery and JSON.NET, and it's designed to work with a ton of open source tools. WPF, despite the litany of MVVM frameworks, and despite it being impossible to develop maintainable WPF applications without one, still hasn't embraced any of them.
- Become open source. ASP.NET MVC was open source since early on, but now the entire ASP.NET stack is open source, and accepts contributions. WPF isn't, and frankly, you wouldn't want to look at the WPF code anyway; it's hideous.
On top of all of this, you've got the innovation that's happening on the web stack itself. Don't like CSS? Try Less or SaaS. Don't like JavaScript? Try CoffeeScript or Dart. There's a rich ecosystem of innovation happening in the web space at the moment, innovation that has never been present in WPF since 2006.
Apples and oranges and all that
I'm not contrasting ASP.NET and WPF in an attempt to say ASP.NET is better, that would be ridiculous, since they clearly serve very different purpose. I'm simply trying to show how one has come so far in six years, while the other has barely changed at all. I think it's all down to a lack of investment.
What's disappointing is that WPF started out quite positively during its time. Concepts like dependency properties, styles, templates, and the focus on data binding felt quite revolutionary when Avalon was announced.
Sadly, these good ideas, when put into practice, didn't have great implementations. Dependency properties are terribly verbose, and could have done with some decent language support. Styles and templates were also verbose, and far more limited than CSS (when WPF shipped I imagined there would be a thousand websites offering high quality WPF themes, just like there are for HTML themes; but there aren't, because it is hard).
Data binding in WPF generally Just Works, except when it doesn't. Implementing INotifyPropertyChanged
still takes way too much code. Data context is a great concept, except it totally breaks when dealing with items like ContextMenus
. ICommand
was built to serve two masters; the WPF team who favored routed commands, and the Blend team who favored the command pattern, and ended up being a bad implementation for both.
XAML
And then there's the failure that is XAML. XAML is so verbose it's hard to imagine that humans were ever supposed to write it. And that's because they weren't! In the land of lollipops and rainbows, designers were supposed to use Blend and developers were going to use the VS designer, and no one would even look at XAML. Yet, it's 2012, and even as Blend has improved most people are still hand-writing XAML. This will not change in VS 2012.
The biggest failing in XAML wasn't that the tooling was bad though; it's that the language was never modified to cope with bad tooling. And unlike HTML, XAML isn't semantic. It's not interpreted. It is compiled, a serialization format, so there's no real separation of the markup and the implementation.
Here's half a dozen things off the top of my head that could be done to improve the XAML experience:
- Allow XML namespace imports to be declared at a project level rather than redeclared in every single file
- Allow binding events directly to methods instead of via commands
- Make the binding syntax shorter and more easily memorable
- Allow C# expressions like basic boolean logic instead of requiring converters all the time
- Allow a boolean to be implicitly converted to the tri-state (dual-state in Silverlight)
Visibility
enum without a converter. - Stop making me use XML prefixes for my own custom controls
The ASP.NET team were able to create an entirely new parser (Razor) for their platform; why can't even minor changes be made in WPF?
MVVM
I can't begin to tell you how tired I am of hearing about this pattern, especially from ex-WinForms developers who think it is the bees knees because they read Silverlight Unleashed and were amazed by MVVM Light.
The reality is that every WPF project I've been brought on to has involved some guy thinking he was smart enough to invent his own MVVM framework, only for it to be a half-baked knock-off of someone's Code Project article. All WPF projects end up with a ViewModelBase
that is so choc full of inherited members for threading and progress bars and INotifyPropertyChanged
. Showing a view in a dialog takes 20 times more code than it would if you just put the code in Button1_Click
, and it's equally as well tested since most people using MVVM are doing it because they claim it is testable but no one is actually writing unit tests for their view models, except the architects inventing the MVVM frameworks in the first place.
There's plenty of hype about MVVM out there, but the lack of platform support for it mean that every WPF developer will need to build a few bad, hard-to-maintain WPF applications before they figure out how to do it properly. That's a real shame.
Conclusion
Ultimately, as I look back over six years of working with WPF, I feel that it was a bunch of good ideas, that weren't very well implemented. You could say the same for the first versions of ASP.NET too (anyone remember Web Parts in ASP.NET 2.0?).
Only there's a big difference: ASP.NET evolved. The web stack evolved. Not only did the code change; the philosophies of the team changed. WPF on the other hand hasn't had a major change since 2006.
What's really sad is that there really isn't an alternative. In the web world, ASP.NET competes with Ruby and PHP; if I don't like one I can switch. On the Windows desktop, I'm pretty much stuck with WPF. And if WPF isn't evolving, that's a real shame.
You might enjoy working with WPF. You might think XAML is a beautiful, terse, fun to write language. That's how I felt in 2006, and if the platform is still enjoyable for you, that's great. It's going to be around for a long time, since there's really no alternative yet. But for me, I'm glad my WPF days are behind me, and that my full time job now is now ASP.NET-focused.