BrainHzSoftware.com

Top 10 problems with WPF

Posted by Scott Reed on 1st November 2007

Don’t get me wrong, I love WPF. I think it is a great technology, and I will continue to leverage it on UI applications that I happen to write. That said, WPF could have definitely benefited from an architecture and consistency team like they had for the BCL in the beginning days of .NET itself. It feels too much like WPF was designed like a bunch of individuals rather than a team. So without further ado here is my list of top ten problems in WPF.

#10 – DockPanel.Dock property does not have a value of Fill. You might be asking, Yeah? So what? Well, instead it assumes by default that the LastChildFill=true. And what that means is instead of laying out the Xaml as top, middle, bottom, or even left, middle, right, you have to lay it out as top, bottom, middle and left, right, middle. Just less readable than it should be. In addition it makes switching from one panel to another difficult. Let’s say you want to move the StackPanel to a DockPanel? Well you not only have to add the properties, you also have to move things around, to get the filled element in the correct position.

#9 – RelativeSource binding is not powerful enough and is too verbose Do you ever assign RelativeSource to anything but a RelativeSource? Can you? So, why do I have to specify it twice? Why not just Source={Relative}? Why isn’t Self just a separate MarkupExtension? Why do I have to go through the whole RelativeSource binding just to get to myself? Also while we are on the subject – what happened to Sibling binding?

#8 – No overriding parts of templates. If there is one thing we should have learned in Software Engineering by now – it is the tried and true principle of DRY (Don’t repeat yourself). But in order to modify a template you have to grab the existing template paste a copy of it and start hacking away. It doesn’t matter if you only need to change one little aspect, you need the entire template. And some of these templates can get really large – like NavigationWindow or Expander for example.

#7 – The Grid is cumbersome to define and refactor. This one really bugs me. In order to create a grid that has multiple children, one needs to define a set of row definitions and column definitions. Annoying, but not the real problem. The real problem is that the children don’t auto-increment. Each child must specify the Grid.Row and Grid.Column that they need to be placed in. There are a couple of problems here: the first is it is just a lot of typing. But the more insidious problem is that in order to later add a column or a row all of the subsequent columns or rows must be incremented. Contrast this with the way HTML tables work – the new column or row can be added in later and no code has to be altered in the process. I understand the power of the grid as it has been specified. However, it would be nice if that power came with some simplicity. To fix this we need four more properties on the grid itself: NumberOfRows, NumberOfColumns, AutoIncrementColumn, and AutoIncrementRow – where the last two are mutually exclusive (you can only set one). Problem solved, no need to declare the lengthy Row and Column specifications, or the Row and Column specifications (unless you are actually doing something which requires the complexity)

#6 – There is NO Rule #6 (couldn’t resist – I am a big Monty Python fan). Actually the problem is, Validation is per binding. What this means is that there is no good way of doing validation where the business rule encompasses more than one piece of data. For example if there are two TextBoxes and the sum of the values is greater than some amount. All of that logic has to be coded in a custom way.

#5 – No way of overriding Equals for ItemsSource and SelectedItem. It is pretty amazing how often I run into this one, and how easy the fix would be. When you set the ItemsSource to be a collection obtained from some ORM layer, and then in another context you grab another instance of one of the items in your ItemsSource you can’t simply set the SelectedItem equals to that new instance, unless it happens to be an immutable object that overrides Equals. In looking for a solution for this many people are recommending that if you control the object (not always the case) you should override Equals. But this is a really bad idea if the identifying properties can change. The right way to fix it is to search through the items from the ItemsSource and select one whose properties match the object you have retrieved. But it would be lovely if we had a way of setting a method to determine equality just like we have ways of sorting, grouping and filtering on the CollectionViewSource.

#4 – Observable list problems. If you do any binding to collections you have probably come across the ObservableList. Lovely collection, it fires events when changed. There are only two drawbacks that make this collection hard to use in some situations: 1) It can only be changed (item added or deleted) on the dispatcher thread 2) You can’t change the collection in response to a collection changed event These two problems are extremely annoying and result in convoluted workarounds

#3 – No easy way of asking what is the visual representation for this item if it is being represented by a DataTemplate. Or what is the item for this visual representation for that matter. Yes it can be done. But it involves some yucky fragile code. It would be great if the ItemsControl base class was able to add a method which wrapped this logic – something like listBox.GetVisualFromItem(someObject);

#2 – Death by a thousand paper cuts (inconsistencies): Many of these examples were taken from Adam Nathan’s excellent book “WPF Unleashed”. There are good reasons for all of these, and in some cases they are an absolute necessity. I am just saying that a newcomer can be quickly overwhelmed by such details. Here are some inconsistencies that new users may face when beginning XAML, followed by a question they might ask when confronted by the inconsistency.

  • You should x:Key to set the identifier for an element in the Resources, but x:Name on an element in the body of the XAML. When do I use x:Key versus x:Name?
  • FrameworkElement and FrameworkContentElement have a Name property, but if a class derived from either of these is implemented in the same assembly, you must set the x:Name attribute rather than the Name attribute. When should I use x:Name versus just Name?
  • The custom namespace syntax is confusing. Is it colon equals? Or equals colon?
  • The child of a given element in XAML sets a property that has been marked with [ContentProperty]. Sometimes its Text, sometimes Content, sometimes Items, sometimes Children (in fact sometimes it is not the Content property at all but simply a TypeConverter). What is actually going on here? How do I do this same thing in code?
  • .NET property wrappers are bypassed at run-time when setting dependency properties in XAML. Why isn’t this called?
  • Only EventTriggers can be attached to controls. How do I change the UI when this event happens?
  • The behavior of property value inheritance can be extremely difficult to track down in many cases (in other words finding out which of the eight providers set the value). Why isn’t this working? Who is setting this value?
  • Instead of setting a dependency property from the code behind, in many cases you should be clearing the value of code behind. Why doesn’t this look right when applying other themes?
  • Attached properties can be set on elements that don’t have that property. In this case a new user won’t be asking anything, they will just assume it can’t be done.
  • ListBox has a SelectionMode property as well as SelectedItem, SelectedValue, and SelectedItems. I am using Single mode, why do I still see SelectedItems? What is the difference between Multiple and Extended? What is the difference between SelectedItem and SelectedValue?
  • ComboBox IsReadOnly and IsEditable are very confusing. Which one do I set?
  • Setting InputGestureText doesn’t enable the Input gesture. Why isn’t this working?
  • TreeView isn’t a Selector. How do I enable Multiple selection on a TreeView?
  • FrameworkElement has Height, Width, ActualHeight and ActualWidth properties. When do I use one versus the other? Why can’t I set the ActualWidth/ActualHeight?
  • ActualHeight and ActualWidth can only be relied upon in the LayoutUpdated event. Why do ActualHeight and ActualWidth work sometimes but not others?
  • Non-Native WPF elements don’t support transforms. Why doesn’t the transform work on this element?
  • Margin as well as HorizontalAlignment and VerticalAlignment properties don’t behave correctly in certain panels. Why isn’t this spacing/laying out correctly?
  • WPF contains both a Grid and a Table. When should I use Grid versus Table? Why can’t I do X inside a Grid or Y inside a table?
  • In WPF there is no ColorDialog, FontDialog, or FolderBrowser. How do I show a Color, Font or Folder Browser dialog?
  • There are a number of choices for Build Action including Resource, Content, and EmbeddedResource? Why can’t I find this resource?
  • There is an IsCancel=true, but no IsOK=true. How do I indicate a button is an OK button?
  • Loose XAML files at the site of origin have an *extremely* confusing syntax. Even experts have to look it up *every single time*. WTF is up with the siteOfOrigin syntax? How many comma’s again? Is it colon, comma, comma, slash or slash, comma, comma, comma, colon?
  • WPF provides two ways to access logical resources StaticResource and DynamicResource. When do I use what? What is the difference between the two?
  • There is an esoteric XAML property called x:Shared that can be placed on a resource to allow it to be used twice. Why am I getting strange errors when they try to include the same resource twice? How do I fix it?
  • The Binding syntax offers the most specific property in the constructor which can lead to confusing syntax. Why do the bindings sometimes use {Binding Source=Blah Path=Bluh} and sometimes {Binding Bluh, Source=Blah}?
  • Although the source property can be any .NET property the target property must be a dependency property. Why can’t I bind this property I created?
  • When binding to an entire UIElement you might inadvertently be attempting to place the same element in multiple places of the visual tree. Why doesn’t this work?
  • There is an IsVisible property which is a boolean, but it is not settable. I have a boolean value, why can’t I bind to IsVisible?
  • ItemsControl’s Items and ItemsSource can’t both be set. How do I add items to my ItemsSource?
  • IsSynchronizedWithCurrentItem only supports single selection. So how do I do master detail with multiple detail views?
  • DataBinding errors don’t throw exceptions. Why did this binding fail?
  • Views are associated with source collections not with the UI displaying the view, so changing the sort, group or filter affects all UIs showing the source. Why does my second control only see what the first one sees?
  • [Related to above] However, unless you set IsSynchronizedWithCurrentItem the SelectedItem is not propogated across multiple UIs. Why doesn’t my other control get the new select value?
  • [Related to above] Changing the current item on a custom view does propogate across multiple UIs; the Selector’s IsSynchronizedWithCurrentItem property must be explicitly set to false to opt out. Why does my other control’s selected item change automatically?
  • There is no way to chain converters, which can lead to combinatorial converter explosion.
  • Returning Binding. DoNothing from a ValueConverter is completely undiscoverable.
  • Some properties default to TwoWay binding, but others default to OneWay. Why isn’t the change getting saved to my property? Or Why *Is* the change getting saved to my property.
  • There are SourceUpdated and TargetUpdated events, but they only get called when the binding has a NotifyOnSourceUpdated or NotifyOnTargetUpdated set to true. Why is it not notifying me of the change?
  • ObjectDataProvider has an IsAsynchronous and Binding has an IsAsync. Why are they different?

None of these thing are major items. In fact most of them make perfect sense once you understand what is going on. But it all adds up to a learning curve that is incredibly steep.

#1 – The ComboBox absolutely sucks ComboBox doesn’t support a TextChanged event, or a SelectionChang*ing* event. These are two absolutely essential elements of the ComboBox, which means you almost always have to create your own. There are a couple of other minor issues like: IsEditable and IsReadonly are confusing (see above). In addition there is no good way to bind if you are editable but your ItemsSource is bound to a property. To make matters worse, when you try and fix these problems by subclassing ComboBox you will find that most of the important methods that you would love to be able to override are actually marked internal.

[Update 2010-05-25] If I had to do this list over again, I would find room for the troublesome fact that ICommand is in the PresentationCore assembly and the System.Windows.Input namespace. What is the problem here? Well I like to make it easy for developers to tell the difference between the View and the ViewModel. Ideally the ViewModel would contain a number of Commands, but that requires knowledge of the UI layer that it was built for. One could say only the PresentationCore assembly and that namespace are allowed in the ViewModel. But once they are included, it is pretty easy to “accidentally” include other UI aspects into the ViewModel as well.

Tags: ,
Posted in Xaml | Comments Off