我有这个标记扩展
public class NullableExtension : TypeExtension
{
public NullableExtension() {
}
public NullableExtension( string type )
: base(type) {
}
public NullableExtension( Type type )
: base(type) {
}
public override object ProvideValue( IServiceProvider serviceProvider ) {
Type basis = (Type)base.ProvideValue( serviceProvider );
return typeof(Nullable<>).MakeGenericType( basis );
}
}
旨在提供其他类型的可空版本。当在“普通”XAML中使用时,它按预期工作。例如,如
<SomeControl DataContext="{My:Nullable System:Int32}"/>
(假设My是为持有扩展名的C#名称空间定义的XML名称空间,对于System来说类似)。正如我所料,控件的数据上下文设置为System.Type
Nullable<int>
。
但是,当我使用此扩展程序尝试设置DataType
的{{1}}属性时
DataTemplate
编译器告诉我,
字典的键不能是类型 'System.Windows.Controls.Primitives.TextBlock'。只有字符串, 支持TypeExtension和StaticExtension。“
和
“没有类型'NullableExtension'的构造函数有1个参数。
有人知道为什么只允许那三种方法(甚至不是<DataTemplate DataType="{My:Nullable System:Int32}">
<TextBlock ... />
</DataTemplate>
的子类,就像我的那样)?那时处理XAML有什么特别之处?还有另一种方法可以实现这一点(基于可以为空的类型的数据模板选择)而无需诉诸TypeExtension
吗?
答案 0 :(得分:12)
我真的遇到了你的问题,这就是我找到的。
问:为什么只允许这三个人(
String
,TypeExtension
和StaticExtension
)?
A:按设计。如果您可以编写任何自定义标记扩展以用作字典中的键,那么会引入哪些副作用?考虑你有Binding作为DataType的值...我很确定你可以添加与字典键动态性质相关的十几个问题。
问:当时处理XAML有什么特别之处?
<强> A 即可。那时你有BAML创建。问题来自内部类BamlRecordWriter
,但该消息并未描述实际问题。当您将自定义标记扩展指定为DataType时,它需要一个DataTemplate的子,并检查它是否可以从string,TypeExtension或StaticExtension分配(参见 BamlRecordWriter.WriteElementStart()函数) 。确实。不是您的扩展名(可分配给TypeExtension),而是第一个子级(不可分配)。现在你有这个奇怪的“不能是”的东西。虽然它看起来像是BamlRecordWriter的错误,但我认为他们故意离开了它。直到它不允许您使用自定义标记扩展作为DataType值,谁在乎错误消息?
问:还有其他方法可以实现这一点(基于可以为空的类型选择数据模板)而无需借助DataTemplateSelector吗?
A:是的,有点儿。首先,你可以让标准的TypeExtension为你做所有的脏工作:
<DataTemplate DataType="{x:Type TypeName=System:Nullable`1[[System.Int32]]}">
</DataTemplate>
但在大多数情况下(如果不是所有的话)你都不会看到模板结果。为什么?现在谈到可以为空的类型的装箱规则。装箱非空可空值类型框表示值类型本身,而不是包装值类型的System.Nullable。因此,默认模板选择器将查找DataType为T
而不是Nullable<T>
的DataTemplate。
我可能无法理解您尝试使用可空扩展解决的确切问题,但您可能希望将nullables包装到您自己的ref类型中,为包装器编写一个DataTemplate并使用DataTemplate.Triggers
来选择内容外观。好吧,这看起来像重新发明的数据模板选择器:)...
注意:我不是MS人,我的调查结果基于Reflector和我自己的经验(这并不像我希望的那样大 )。无论如何,希望我能帮忙:)。
干杯
答案 1 :(得分:2)
语法
DataType="{x:Type TypeName=System:Nullable`1[[System.Int32]]}">
似乎不适用于用户定义的类型:(
实际上另一种方法是创建一个基本的非泛型类型。将第一个数据模板设置为该类型,并将ContentPresenter.Content
绑定到包含T
对象的属性。然后为T
创建其他数据模板。
答案 2 :(得分:2)
我找到了一个讨厌的解决方法。无论出于何种原因,@ Anvaka都是对的:DataType属性不允许您使用自定义MarkupExtension。但它允许您使用自定义MarkupExtension的StaticResource。
使用MarkupExtension,向其添加一个公共默认构造函数。然后在资源中创建扩展的实例,直接设置属性。热潮,它需要它。以下内容类似于您需要做的事情
<My:Nullable x:Key="Foo" Type="{x:Type System:Int32}"/>
<DataTemplate DataType="{StaticResource Foo}">
<TextBlock ... />
</DataTemplate>
答案 3 :(得分:1)
这应该有用......
<DataTemplate DataType="{x:Type System:Nullable`1[System.Int32]}">
</DataTemplate>