Как ссылаться на общий тип в атрибуте DataType DataTemplate?

У меня есть ViewModel, как это:

 public class LocationTreeViewModel<TTree> : 
               ObservableCollection<TTree>, INotifyPropertyChanged
                                                    where TTree : TreeBase<TTree>

Я хочу ссылаться на него в атрибуте DataType DataTemplate в XAML. Как я могу это сделать?

Ответ 1

Единственный способ сделать это - использовать MarkupExtensions.

public class GenericType : MarkupExtension
{
     private readonly Type _of;
     public GenericType(Type of)
     {
         _of = of;
     }
     public override object ProvideValue(IServiceProvider serviceProvider)
     {
         return typeof(LocationTreeViewModel<>).MakeGenericType(_of);
     }
}

И для его использования мне просто нужно это сделать:

<DataTemplate DataType="{app:GenericType app:TreeBaseClass}">

Ответ 2

Нет, вы не можете выразить тип генериков в XAML. Вам нужно будет создать конкретный тип, который расширяет ваш общий...

public class FooLocationTreeViewModel : LocationTreeViewModel<Foo>
{
}

Ответ 3

В XAML 2006 это не поддерживается. Однако, если вы хотите иметь эту функциональность, вы можете свернуть самостоятельно.

Эта ссылка содержит хорошее руководство по созданию расширений разметки.

Использование будет таким:

<Grid xmlns:ext="clr-namespace:CustomMarkupExtensions">
  <TextBlock Text="{ext:GenericType FooLocationTreeViewModel(Of Foo)}" />
</Grid>

Однако вы должны выбрать и реализовать синтаксис. Я предлагаю обозначение VB, так как оно не будет мешать, как обозначение С# с < и > .

Ответ 4

Расширение разметки {x: Тип} позволяет разрешать общие аргументы типа в виде списка, разделенного запятой, в круглых скобках.

Вот пример:

<UserControl x:Class="Test"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type generic:List(sys:Int64)}">
            <TextBlock Text="{Binding Count}"/>
        </DataTemplate>
    </UserControl.Resources>
</UserControl>

Я использую.Net 4.5 на VS 2015, поэтому ваш пробег может отличаться.

Ответ 5

Немного улучшенная версия MarkupExtension, работайте для классов до 3 общих параметров.

  public class GenericTypeExtension : MarkupExtension
  {
    public GenericTypeExtension()
    {

    }
    public GenericTypeExtension(string baseTypeName_, Type genericType1_, Type genericType2_, Type genericType3_)
    {
      BaseTypeName = baseTypeName_;
      GenericType1 = genericType1_;
      GenericType2 = genericType2_;
      GenericType3 = genericType3_;
    }
    public string BaseTypeName { get; set; }
    public string BaseTypeAssemblyName { get; set; }
    public Type GenericType1 { get; set; }
    public Type GenericType2 { get; set; }
    public Type GenericType3 { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider_)
    {
      var list = new List<Type>();
      if (GenericType1 != null)
      {
        list.Add(GenericType1);
      }
      if (GenericType2 != null)
      {
        list.Add(GenericType2);
      }
      if (GenericType3 != null)
      {
        list.Add(GenericType3);
      }

      var type = Type.GetType(string.Format("{0}`{1}, {2}", BaseTypeName, list.Count, BaseTypeAssemblyName));
      if (type != null)
      {
        return type.MakeGenericType(list.ToArray());
      }
      return null;
    }

  }