Referring to nested types in XAML
Anyone who has programmed using the WPF framework for a while eventually runs
into its limitations. In my opinion, the framework is great overall; it offers
many advantages over WinForms, and for little extra overhead. However, the
limitation of {x:Type}
to only reference classes that are direct descendants
of a namespace was bothering me, so I made a markup extension to overcome the
issue.
At the thousand-foot view, the WPF framework has many conveniences for the programmer. In particular, the ability to create arbitrary events and properties on objects really makes it easy for the interaction logic to closely match the implementation logic. Plus, forcing the MVVM pattern makes things a bit more consistent between projects and developers. But not allowing nested types to be referenced from XAML is the sort of nitty-gritty implementation detail that gets on my nerves.
I was able to create a new markup extension that provides this functionality:
using System; using System.Windows.Markup; namespace SomeNameSpace { public class NestedTypeExtension : TypeExtension { public NestedTypeExtension() { } public NestedTypeExtension(string typeName) : base(typeName) { } public override object ProvideValue(IServiceProvider serviceProvider) { string[] types = TypeName.Split('.'); IXamlTypeResolver resolver = (IXamlTypeResolver)serviceProvider.GetService(typeof(IXamlTypeResolver)); if (resolver != null && types.Length > 0) { Type t = resolver.Resolve(types[0]); for (int i = 1; i < types.Length; i++) { t = t.GetNestedType(types[i]); } Type = t; return t; } return null; } } }
Then in XAML you can use the extension by adding the namespace to a parent element
<Element xmlns:sns="clr-namespace:SomeNameSpace;assembly=SomeAssembly" > ...
and referencing the markup extension with
<DataTemplate DataType="{sns:NestedType sns:MyClass.MyNestedClass}"> ... </DataTemplate>
Intellisense will still complain that nested types are not supported, but it
will compile and run just fine. Unfortunately even with this, it is not possible
to set x:Key
of a ResourceDictionary
, since that is only allowed to be
exactly a string
, TypeExtension
, or StaticExtension
(in XAML). So if you
want to use a nested type as a key, I recommend giving the resource a fixed name
and adding some code to the type's static constructor to assign the key:
namespace SomeNameSpace { class MyClass { class MyNestedClass { static MyNestedClass() { MyDictionary.Add(typeof(MyNestedClass), MyDictionary["MyNestedClassKeyName"]); } } } }
For the purposes of organization, I set the key name to be the string
representation of the nested class. For instance, the key name I would use for
the example above would be "MyClass.MyNestedClass"
.
Remember that if you're trying to insert a datatemplate to be used for instances
of the nested class, the key should be new DataTemplateKey(typeof(MyNestedClass))
.
This had me scratching my head for a little while until I figured it out.
Comments