Our WPF started to hang when opening certain window, and debugging revealed an InvalidOperationException
with this mysterious message. After some digging I found that we had code along the lines of
...
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
...
<Style TargetType="{x:Type xctk:DoubleUpDown}" x:Key="bla" ... />
...
<xctk:DoubleUpDown Style="{StaticResource bla}" ... />
Somehow it happened, that the control was coming from one assembly: Xceed.Wpf.Toolkit.DoubleUpDown, Xceed.Wpf.Toolkit, Version=1.9.0.0
, and the style was targeting a type in another assembly: Xceed.Wpf.Toolkit.DoubleUpDown, WPFToolkit.Extended, Version=1.8.0.0
.
These two assemblies are in fact two versions of Xceed WPF toolkit: they contain the same classes assigned to the same XML namespace. Our mistake was to reference both the old and the new assembly, which led to confusion.
When we say xctk:DoubleUpDown
, it may map to either of those assemblies, and I am not sure how exactly WPF makes the choice, but apparently the choice may be inconsistent and thus lead to cryptic error messages.
There are several takeaways/anti-patterns that played a dirty trick on us in this case:
- Xceed should have changed XML namespace, and also the CLR namespace when they changed the assembly name. Creating two different assemblies with the same classes is asking for trouble.
- WPF should have included full class information somewhere in the error message. Don’t assume that class name or even fully qualified class name uniquely identifies the class.
- Painting errors should not be fatal. The above XAML error triggered an exception during ‘measure’ phase of each re-paint cycle, effectively hanging the application. There should have been a better way to recover from it, e.g. show an empty window or something.