Sunday, July 3, 2011

What is wrong with WPF?

When WPF was first announced, I got me extremely excited.  I watched a number of the early demos and saw what was possible in terms of styling and animation and it just seemed amazing.  One demo showed how simply a ListBox’s template could be changed to display as a map of the world and each item was a country. Combine that with the promise of rich media support and hardware acceleration and I was convinced WPF was going to be the best thing ever.

Such was my excitement that I started working with WPF as soon as I could get my hands on it.  I have been working with it professionally since 2006 and over that time I have found a number of problems that routinely rear their ugly heads. I find it hard to stop thinking about a problem until I have a solution, but the nature of the problems combined with the fact WPF closes up so many possible extension points means I haven’t been able to come up with a solution using WPF as we know it today.

This combined with my OCD desire to solve problems has caused me to spend a lot of the time I should be sleeping thinking about how I could design a successor to WPF that was free of these problems.  Unfortunately I don’t have enough spare time to actually build the thing so I asked the tweeps to convince me I shouldn’t waste my time by starting to write a replacement. @jtango18 asked me why I would want to in the first place and this post will attempt to answer that question.

Poor design experience

One of the much touted features of WPF was supposed to be how it enabled designers and developers to focus on what they were good at.  Blend being the tool for designers and Visual Studio being the tool for developers.  The problem is that Blend is only any good for doing a design when you have some data to display and tends to fall down quite badly if you have certain constructs in your XAML or code behind.  Everything will work when you run the application but the designer just blows up.

Blend does have tools for generating some design time data, but this is limiting because of the time it takes to set it up and it also won’t work if you have different data templates which are selected based on the type of the object you are displaying.  There are many other small annoyances with using the designers, but the thing that frustrates me the most is that I often have to run my app, navigate to the part I am working on look at it, then go back and tweak the design and repeat infinitum.  When you are trying to decide on the best margin or colour for something in a data template I find my self wasting enormous amounts of time repeating that cycle.

My UI framework would be built with this in mind and would allow the designer to attach straight to the running process and edit the view live.  You could make use of the real data and get instant feedback as you made changes even when you are 3 or 4 templates deep.  I think this might actually be achievable with WPF as some of this can be done using tools like Snoop, but it would take a lot of work to get it to the same level of functionality that Blend supports.

Poor Animation Performance

I simply cannot understand this one, but I suspect it boils down to rendering performance.  I have often found animations involving a few objects, especially objects that have a few effects applied (eg drop shadow) do not animate smoothly into position.  I refuse to believe that my laptop is incapable of rending a few UI vector shapes moving around with a couple of simple effects applied smoothly.  For crying out loud it can render this @ 60 frames per second at 1920 x 1200 and it has to do extra work like updating physics for 100s of objects:

982059671

Animations Fail To Play

Quite often when you have a UI component that has different states you would like it to look different in each state.  In .NET 4.0 the VisualStateManager was added to help with this sort of thing.  However I sometimes find animations between states, or animations that should loop while in a given state simply fail to play some of the time.  I have never been able to get it to work 100% of the time, it will work most of the time and then just fail for no apparent reason.

I encountered similar issues before the VisaulStateManager was added to the framework and in those situations it boiled down to not being able to apply the animation, because another animation was already applied to the object.  Given that animation was one of the key things WPF was supposed to make easier it is more than a little silly how many workarounds I have to use to get it to work properly.

Drag and drop

There is no inbuilt support for doing drag and drop with nice Windows shell effects, such as drag descriptions and thumbnails.  WPF is supposed to be about creating rich user experiences, but it doesn’t even let you easily do shell style drag and drop.  I have implemented a bunch of Blend behaviours which make this possible, but you wouldn’t believe how much trouble and how many hacks I had to use to get them to work properly without flickering.  Not to mention how MS decided to make the methods for accessing the mouse’s screen coordinates internal, you need the screen coordinates to prevent accidental drag and drop operation when the view is scrolling.

Missing effects

When you are trying to design a nice looking UI, gradients and drop shadows can go a long way.  Inevitably though you wind up wishing you could use a bevel, inner shadow, inner glow and maybe an outer glow.  If you have worked with Photoshop you will know how these effects can be used to create some quite striking visuals, but alas WPF doesn’t have support for them.

You can off course get some of these by using the old bitmap effects, which works fine as long as the object you are applying it too is tiny, but as soon as you add it to a big object things start to chug very very badly.  No worries I hear you say, you can just write your own graphics shader to do it for you.  Unfortunately WPF doesn’t support multi-pass shaders which are required for these effects.  Well actually it sort of does, the built in shadow effect does use multiple passes, but they don’t expose that functionality to you so you’re stuck with a single pass.  The end result is that you end up using lots of PNG images made by Photoshop and you can kiss your resolution independence goodbye.

Video and other media support is poor

I have worked on a couple of projects where the level of support for video playback in WPF has been very disappointing.  In both cases I ended up having to ditch WPF and go back to WinForms in conjunction with DirectShow.  WPF is fine if all you want to do is play a video here and there, but as soon as you want to do more than that you run into problems.

For example say you have multiple videos all playing at once and it is imperative that they stay perfectly in sync with one another.  This is not possible in WPF because your access to the underlying resources is too limited.

Another example, you would like to have one video playing in a loop and then, when the user presses a button immediately start playing another video with no noticeable pause between the videos.  However there is no way to tell WPF to buffer a video other than to play it, you can play it and pause it, but you still don’t know when the buffering is complete so it is hard to transition between the two.

Triggers are messed up

When I was first learning about triggers I thought they were awesome, but after you learn the limitations it starts to disappoint you.  For example you can’t use data triggers outside of a data template, why is that?  Then there is how you can’t say you want the trigger to fire when the value is not equal to the one you provide.  I actually spent quite a bit of time digging about in Reflector for a way around that one and eventually discovered that underneath it all triggers actually do support the idea of not equal, but it is never exposed anywhere.  I couldn’t make a nice fix for it though, because the trigger collections test to make sure they only receive specific implementations.  That just plain sucks, why can’t I write my own triggers? 

Some would say so I can’t so that I don’t write triggers that perform badly and then blame MS for poor performance, but I think that is the stupidest reason ever.  By giving me more power, sure it does increase the ways I can blow my head off, but it also lets me do amazing things that no one has thought of before.  Given that I’m a responsible adult let me decide if I want to play with fire or not, I won’t blame you if I burn my self.

Implementing Virtualizing Panels is Hard

So you have a list that has hundreds or thousands of items and you want to scroll smoothly.  You can sort of get away with the built in virtualizing stack panel, but it isn’t smooth and if you have your own layout you can forget about it all together.  We need a way to solve this problem that doesn’t take many, many hours to get something that is passable.

Binding Memory Leaks

By default if you bind to a POCO object that doesn’t implement INotifyPropertyChanged you get a memory leak, where the object you are binding too will never ever be garbage collected.  This is crap and should not happen.

Lack of composition and extension points

WPF was supposed to be all about composition over inheritance.  Control and Data templates do to some degree achieve this goal, but I just wish they had applied the same thinking to the rest of the framework.  For example the Button control’s inheritance hierarchy has eight classes before you get back to object. There is a school of thought that suggests that if the team had realised the power of attached properties earlier a lot of the functionality would be implemented as something like Blend behaviours instead of separate classes.

This inheritance tree can makes things rather difficult, because quite often you end up having to have a case in your code for both FrameworkElement and FrameworkContentElement, why the hell they don’t implement a common interface I have no idea.

When you put this together with so many places where your ability to extend the framework has been deliberately limited (eg triggers, low level video access and multi-pass shaders to name a few) you end up with a sour taste in your mouth.  I have spent many hours over the last few years getting around these problems in software projects I have been working on, but if WPF was just built a little differently I wouldn’t have the problem or there would be an elegant solution rather than painful hacks.

In Conclusion

So am I crazy and these problems only exist for me or have you had these problems too?  Feel free to leave a comment or haggle me @calebvear on twitter, I would love to hear about your experiences.

PRIEMTIVE NOTE: I have heard some of the news about Windows 8 and the new DirectUI and Jupiter APIs. Of course at the moment most of what I have heard seems to be largely speculative. I do hope that it will solve many of the issues I have today, but I doubt it will solve all of them.

8 comments:

  1. Totally agree with all that you have mentioned, there's more to it, you've really only scrapped a fraction of the pain points of all the WPF devs out there.

    It's really ironic how they touted it to be a framework to deliver rich and great experiences, yet it's a shit experience for devs to deliver this.

    I don't know how many times have I been so excited of whacking in some dropshadoweffect's and liking the design then overtime it grounding to a halt in perf, then take them all back out, I just wasted time again.

    Creating custom chrome windows with a nice experience, i.e. maybe an OS drawn dropshadow? Is a bitch and a hack.

    Completely agree with the triggers too, It sure is stupid and deceiving, so if you haven't fixed it all by this release maybe renamed the freking properties to DataTriggers and PropertyTriggers, rather than just Triggers, make a specific property for each bloody trigger type you're going to let me shove in you, and deceive the hell out of me.

    Don't even talk about memory leaks, Binding is so powerful yet so brittle with the leaks, and for gods sake, I'm sick of having to raise notify property changes for auto properties, come on MS, you're good at emitting code at compilation, give me a bloody keyword or an attribute.

    And yes, animations suck balls, despite all the awesome demos they had playing in the past.

    Basically, WPF is a huge fail, they fail from a performance perspective, they fail from over abstraction, fail from closing it off from extensibility, and they fail for their deceiving API's that are limited.

    ReplyDelete
  2. And text in WPF *still* doesn't look as nice as in Win32 or even WinForms.

    ReplyDelete
  3. @jwatte_food, disagree with that one, that did fix that for good, if you explore the TextOptions property, you can make it possible that your text looks exactly like WinForms text.

    ReplyDelete
  4. @Winston, I agree that I am only scrapping the surface with this post. It would have been too long if I went into much more detail, but these were the things that have hurt me the most.

    The thing that is most irritating to me is that I think all of these problems could be solved, but it seems Microsoft just isn't interested. If I ever get enough free time (seems doubtful) I might have a crack at my own UI framework :)

    ReplyDelete
  5. @Winston,

    I almost forgot regarding auto properties and raising property changed events you should check out http://code.google.com/p/notifypropertyweaver/ as that pretty much solves the problem for you.

    ReplyDelete
  6. Have you ever tried modifying the way tabbing and keyboard navigation of controls works in WPF? Or adding keyboard navigation to a custom control. There's also little to no documentation about this either.

    ReplyDelete
  7. @Cameron: I found the best way to delve into WPF is to just take one of the Microsoft Press books on it and read it cover-to-cover.

    The style, trigger and binding stuff is pure awesomeness and without knowing all your options, one tends to work WPF as if it was just WinForms with XAML.

    For keyboard navigation, use an underline instead of the et. Also, learn WPF commanding (ICommand, CommandBinding) which allows you to assign "gestures" (which are keyboard shorcuts) to commands (like ApplicationCommands.Save or your own ones) and then just declare an application-wide, window-wide or even control-wide handler for that type of command.

    ReplyDelete
  8. @Caleb, thanks, I've seen that property weaver, I'm currently using a different technique that uses the castle dynamic proxy generator that ties with a interceptor, that does the same thing, you just need to flag the properties with virtual.

    Both the same I guess :)

    I know, Microsoft can fix this, but they're not focused on it right now, it's quite sad, but oh well :(

    ReplyDelete