使用自定义类型扩展名指定DataTemplate.DataType

时间:2009-12-13 20:11:59

标签: wpf xaml datatemplate

我有这个标记扩展

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吗?

4 个答案:

答案 0 :(得分:12)

我真的遇到了你的问题,这就是我找到的。

  

问:为什么只允许这三个人(StringTypeExtensionStaticExtension)?

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和我自己的经验(这并不像我希望的那样大alt text )。无论如何,希望我能帮忙:)。

干杯

答案 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>