2009-12-13 73 views
8

指定DataTemplate.DataType我有这个标记扩展一个自定义类型的扩展名

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}"/> 

(假设我是类似地对于系统为C#命名空间保持所述扩展中定义的XML命名空间,和)。按照我的预期,控件的数据上下文被设置为System.Type,Nullable<int>

然而,当我使用这个扩展,试图设置的DataTemplate

<DataTemplate DataType="{My:Nullable System:Int32}"> 
    <TextBlock ... /> 
</DataTemplate> 

有人告诉我,由编译器的DataType属性,即

的字典中的密钥无法是类型 'System.Windows.Controls.Primitives.TextBlock'。只有字符串, TypeExtension和StaticExtension的支持。 “

” 无构造函数型 'NullableExtension' 有1个参数。

是否有人知道为什么只有这三种方法(甚至没有像我的TypeExtension的子类)被允许?在这一点上处理XAML的特别之处是什么?是否有另一种方法来实现这一点(基于可能为空的类型的数据模板选择),而不诉诸于DataTemplateSelector

回答

11

我真的进入了你的问题,这里是我发现的。

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

答:由设计。如果您可以编写任何自定义标记扩展名作为字典中的键,那么会引入什么副作用?考虑你有绑定作为DataType的值...我敢肯定,你可以添加与字典键动态性质相关的十几个问题。

问:在这一点上处理XAML的特别之处是什么?

A。那时你有BAML创作。问题来自内部类BamlRecordWriter,但该消息没有描述实际问题。当您将自定义标记扩展名指定为DataType时,它将接收DataTemplate的子项,并检查它是否可从字符串,TypeExtension或StaticExtension分配(请参阅BamlRecordWriter.WriteElementStart()函数)。确实。不是你的扩展(可分配给TypeExtension),而是第一个孩子(不可分配)。现在你有这个奇怪的“不能是”的东西。虽然它看起来像一个BamlRecordWriter的bug,但我认为他们故意离开它。直到它不允许您使用自定义标记扩展作为DataType值,谁在乎错误消息?

问:有另一种方式来做到这一点(基于类型可能为空的数据模板选择),而不诉诸DataTemplateSelector?

- 答:是的,种。首先,你可以有标准TypeExtension为你做最脏最累的工作:

<DataTemplate DataType="{x:Type TypeName=System:Nullable`1[[System.Int32]]}"> 
</DataTemplate> 

但在大多数情况下(如果不是所有的时间),你将不会看到模板的结果。为什么?现在它涉及到可空类型的装箱规则。装箱一个不可为null的值类型会将值类型框本身,而不是包装值类型的System.Nullable。因此,默认模板选择器将查找DataType为T而非Nullable<T>的DataTemplate。

我可能不明白你想用可空的扩展名解决的确切问题,但是你可能想把可换行符放到你自己的引用类型中,为封装器写一个DataTemplate并使用DataTemplate.Triggers来选择内容外观。那么,这看起来像重新发明的数据模板选择器:)...

注意:我不是一个MS家伙,我的发现是基于反射器和我自己的经验(这不是我想要的那么大为alt text http://www.kolobok.us/smiles/standart/blush2.gif)。无论如何,希望我可以帮助:)。

干杯

+0

谢谢。我意识到这可能是危险的,但我认为可以验证分配给DataType的值的类型实际上是System.Type。我没有试图给DataType赋任何任意值,只是一个Type。 有趣的BAML分析器。 至于拳击问题,这是一个很好的观点,虽然在我得到的情况下,我会明确提供一个Nullable 而不是一个盒装值,并且有其他脚手架以使“做正确的事情”我的需求。 看起来我必须继续解决方法。谢谢您的帮助。 – 2009-12-16 05:53:11

+0

呃,我总是忘记注释不要在这里保留额外的换行符。对不起,上面的格式很糟糕。 – 2009-12-16 05:53:45

+1

自写入以来,事情发生了明显变化,并且它看起来像'DataType =“{x:Type TypeName = System:Nullable \'1 [[System.Int32]]}''应该可以在2013年8月工作。 NET 4.0,它没有。我发现解决这个问题的唯一方法就是使用某种MarkupExtension。 – Dan 2013-08-01 20:38:51

1

语法

DataType="{x:Type TypeName=System:Nullable`1[[System.Int32]]}"> 

似乎并没有为用户定义类型:(

其实另外一个方法是创建一个基础非通用型工作,设置第一数据模板,然后将ContentPresenter.Content绑定到保存对象T的属性,然后为其创建其他数据模板T

1

这应该工作...

<DataTemplate DataType="{x:Type System:Nullable`1[System.Int32]}"> 
</DataTemplate> 
+0

要写出更好的答案,您应该为解决方案提供一些背景(为什么它有效?您使用的是什么来源?) – Dhara 2012-06-28 18:25:01

2

我发现了一个讨厌的办法解决这个。无论出于何种原因,@Anvaka都是正确的:DataType属性不允许您使用自定义的MarkupExtension。但它将允许您使用自定义MarkupExtension的StaticResource。

拿你的MarkupExtension,添加一个公共的默认构造函数。然后在资源中创建扩展的实例,直接设置属性。繁荣,它需要它。以下内容与您需要做的相似

<My:Nullable x:Key="Foo" Type="{x:Type System:Int32}"/> 
<DataTemplate DataType="{StaticResource Foo}"> 
    <TextBlock ... /> 
</DataTemplate>