我有一个像这样定义的ViewModel:
public class LocationTreeViewModel<TTree> :
ObservableCollection<TTree>, INotifyPropertyChanged
where TTree : TreeBase<TTree>
我想在XAML中的DataTemplate的DataType属性中引用它。我怎么能这样做?
答案 0 :(得分:11)
不,你不能在XAML中表达泛型类型。您将不得不创建一个扩展通用类型的具体类型......
public class FooLocationTreeViewModel : LocationTreeViewModel<Foo>
{
}
答案 1 :(得分:3)
在XAML 2006中,不支持此功能。但是,如果您想拥有此功能,则可以自行推送。
This link有一个关于创建标记扩展的很好的教程。
用法如下:
<Grid xmlns:ext="clr-namespace:CustomMarkupExtensions">
<TextBlock Text="{ext:GenericType FooLocationTreeViewModel(Of Foo)}" />
</Grid>
您必须选择并实现语法。我建议使用VB表示法,因为它不会像C#符号那样干扰&lt;和&gt;。
答案 2 :(得分:1)
我知道,我参加聚会有点晚了,但是我想为所有将来可能会看到此问题的人提供答案:
有可能。
您可以在以下问题的答案中看到完整的代码:DataTemplates and Generics。但由于时间太长,我只复制重要的部分。如果您想了解更多详细信息,请查看参考的问题。
您需要编写一个MarkupExtension
来提供封闭的泛型类型。
public class GenericType : MarkupExtension
{
public GenericType() { }
public GenericType(Type baseType, params Type[] innerTypes)
{
BaseType = baseType;
InnerTypes = innerTypes;
}
public Type BaseType { get; set; }
public Type[] InnerTypes { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
Type result = BaseType.MakeGenericType(InnerTypes);
return result;
}
}
现在,您可以在xaml中定义关闭通用类型的类型,然后将闭合的通用类型用作DataType
的{{1}}。
DataTemplate
很高兴WPF自动选择了已定义的<Window.Resources>
<x:Array Type="{x:Type System:Type}"
x:Key="ListWithTwoStringTypes">
<x:Type TypeName="System:String" />
<x:Type TypeName="System:String" />
</x:Array>
<WpfApp1:GenericType BaseType="{x:Type TypeName=Generic:Dictionary`2}"
InnerTypes="{StaticResource ListWithTwoStringTypes}"
x:Key="DictionaryStringString" />
<DataTemplate DataType="{StaticResource DictionaryStringString}">
<TextBlock Text="Hi Dictionary"
FontSize="40"
Foreground="Cyan"/>
</DataTemplate>
</Window.Resources>
。
答案 3 :(得分:1)
这个问题的答案并非完全是最新的(CollinE和Bas已经说过这实际上是不可能的)...但是,替代解决方案可能对其他人有帮助:
可以使用类似的TemplateSelector来解析通用类型:
TemplateSelector
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var genericType = typeof(MyGenericType<>);
var isMyGeneric = item?.GetType().GetGenericTypeDefinition() == genericType;
return isMyGeneric ? MyTemplate : OtherTemplate;
}
public DataTemplate MyTemplate { get; set; }
public DataTemplate OtherTemplate { get; set; }
}
XAML
<UserControl.Resources>
<DataTemplate x:Key="MyTemplate">
<!-- Set Up MyTemplate -->
</DataTemplate>
<DataTemplate x:Key="OtherTemplate">
<!-- Set Up OtherTemplate -->
</DataTemplate>
<local:MyTemplateSelector x:Key="MyTemplateSelector"
MyTemplate="{StaticResource MyTemplate}"
OtherTemplate="{StaticResource MyTemplate}" />
</UserControl.Resources>
...
<ContentControl ContentTemplateSelector="{StaticResource MyTemplateSelector}"
Content="{Binding ViewModel}" />
答案 4 :(得分:0)
我刚刚实现了一个肯定不完美的解决方法,并且确实需要在ViewModel中添加一些代码(由于VM不应该了解视图,因此这会破坏严格的MVVM)。
定义您的泛型类型,然后使用最低的共同祖先作为类型参数定义该类型的类:
class GenericClass<T> { }
class Class1 : GenericClass<Apples> { }
class Class2 : GenericClass<Oranges> { }
class WorkaroundClass : GenericClass<Fruit> { }
在您的视图模型中,您需要将绑定的成员声明为祖先类型,并将其强制转换。
// instead of:
// Apple DisplayFruit => GetGrannySmith();
Fruit DisplayFruit => (Fruit)GetGrannySmith();
然后在您的xaml中,可以将数据模板绑定到祖先类:
<DataTemplate DataType="{x:Type WorkaroundClass}"/>
我很确定,因为Generic父类是泛型的,所以您不应该遇到类型参数之间的差异会引起任何问题的实际情况。
答案 5 :(得分:-1)
{x:Type}标记扩展支持允许泛型类型参数在括号中指定为逗号分隔列表。
以下是一个例子:
<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>
我在VS 2015上使用.Net 4.5,因此您的里程可能会有所不同。
答案 6 :(得分:-3)
我能做到这一点的唯一方法是使用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}">
答案 7 :(得分:-3)
稍微改进的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;
}
}