Tobias
Tobias

Reputation: 38344

Generic Type in Xaml Resources

I have a generic class like this:

public class GenericClass<T>  where T : class
{
    public GenericClass()
    {
    }
}

And I want this class to hold as a resource in a Window or UserControl. Then I need a GenericTypeExtension for Xaml like this https://stackoverflow.com/a/5403397/376086 which I copied so:

[ContentProperty("TypeArguments")]
public class GenericTypeExtension : MarkupExtension
{
    private Collection<Type> _typeArguments = new Collection<Type>();

    public Collection<Type> TypeArguments
    {
        get { return _typeArguments; }
    }

    // generic:List`1
    private string baseTypeName;

    public string BaseTypeName
    {
        get { return baseTypeName; }
        set { baseTypeName = value; }
    }

    public GenericTypeExtension()
    {
    }

    public GenericTypeExtension(string baseTypeName)
    {
        this.baseTypeName = baseTypeName;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (string.IsNullOrEmpty(baseTypeName))
            throw new ArgumentNullException("BaseTypeName");
        string[] baseTypeArray = baseTypeName.Split(':');

        if (baseTypeArray.Length != 2)
            throw new ArgumentException("BaseTypeName");

        if (TypeArguments.Count == 0)
            throw new ArgumentException("TypeArguments");

        IXamlNamespaceResolver nameResolver =
            serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlNamespaceResolver;
        IXamlSchemaContextProvider schemeContextProvider =
            serviceProvider.GetService(typeof(IXamlSchemaContextProvider)) as IXamlSchemaContextProvider;

        if (nameResolver == null || schemeContextProvider == null)
        {
            IRootObjectProvider rootObjectProvider =
                serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
            if (rootObjectProvider as DependencyObject != null &&
                !DesignerProperties.GetIsInDesignMode(rootObjectProvider as DependencyObject))
                throw new Exception("This Generic markup extension requires these services");
            else
                return null;
        }

        XamlTypeName xamlTypeName = new XamlTypeName(nameResolver.GetNamespace(baseTypeArray[0]), baseTypeArray[1]);
        Type genericType = schemeContextProvider.SchemaContext.GetXamlType(xamlTypeName).UnderlyingType;
        Type[] typeArguments = TypeArguments.ToArray();

        return Activator.CreateInstance(genericType.MakeGenericType(typeArguments));
    }
}

But why do I get a XamlParseException when I do this:

<Window x:Class="GenericXaml.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:genericXaml="clr-namespace:GenericXaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow"
    Width="525"
    Height="350">
    <Window.Resources>
        <genericXaml:GenericTypeExtension BaseTypeName="genericXaml:GenericClass`1" x:Key="hanswurst">
            <x:Type TypeName="system:String" />
        </genericXaml:GenericTypeExtension>
    </Window.Resources>
    <Grid />
</Window>

The Exception is here: Exception

The Text of the Inner Exception is: enter image description here

What am I doing wrong?

Update: It also doesn't work with:

        <genericXaml:GenericTypeExtension BaseTypeName="generic:List`1" x:Key="kdid">
        <x:Type TypeName="system:String"/>
    </genericXaml:GenericTypeExtension>

Upvotes: 1

Views: 2423

Answers (2)

Eli Arbel
Eli Arbel

Reputation: 22749

What's happening is that WPF attempts to assign your markup extension's value to the Resources property, instead of adding an item to the dictionary. This can be easily solved by:

<Window.Resources>
    <ResourceDictionary>
        <genericXaml:GenericTypeExtension BaseTypeName="genericXaml:GenericClass`1" x:Key="hanswurst">
            <x:Type TypeName="system:String" />
        </genericXaml:GenericTypeExtension>
    </ResourceDictionary>
</Window.Resources>

Upvotes: 4

ad1Dima
ad1Dima

Reputation: 3197

Your MarkupExtension provides instance of type, but x:Key is not set for this instance.

UPD I have idea! You can try to create container like that:

[ContentProperty("Value")]
public class GenericContainer
{
    public object Value { get; set; }
}

and than declare Resource like that:

<Window.Resources>
    <ns:GenericContainer x:Key="hanswurst">
        <genericXaml:GenericTypeExtension BaseTypeName="genericXaml:GenericClass`1">
            <x:Type TypeName="system:String" />
        </genericXaml:GenericTypeExtension>
    </ns:GenericContainer>
</Window.Resources>

It puts some limitations on usage of resource, but I guess it can help.

PS You can inheritance like StringGenericClass:GenericClass<string> to use it in xaml.

Upvotes: 2

Related Questions