WPF DataTemplate在卸载时重置某些依赖项属性

时间:2010-11-24 06:51:34

标签: c# .net wpf datatemplate

我有DataTemplate由媒体元素控件组成,该控件源自WPF Media Kit库中的MediaElementBaseMediaElementBase类提供了两个属性LoadedBehaviorUnloadedBehavior,允许用户指定加载/卸载元素时会发生什么。

我发现在DataTemplate中使用它时(如下所示),在卸载模板时,但在调用Unloaded事件之前,这些属性会重置为默认值,仅表示默认UnloadedBehavior将执行:

<DataTemplate DataType="{x:Type Channels:AnalogChannel}">
    <Controls:AnalogTvGraphFileElement
        LoadedBehavior="Play"
        UnloadedBehavior="Stop"
        Channel="{Binding}" />
</DataTemplate>

当控件只是页面上的元素并且Unloaded通过正常的导航事件发生时,不会发生这种情况。

调试DependencyPropertyChanged EventHandler显示内部方法System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode(在PresentationFramework.dll中)检查DependencyProperty是否可能被继承,如果不是,则使其无效。当然,更改LoadedBehavior / UnloadedBehavior的属性元数据以添加FrameworkPropertyMetadataOptions.Inherits会阻止此属性在模板更改时重置。

有谁知道为什么会这样?我可以添加Inherits标志作为解决方法,因为此元素没有受此影响的子元素,但我想知道为什么/是否正确。

如果您要了解有关我正在做的事情以及我为何要更改DataTemplates的更多信息,您可以查看this question以获取说明。

1 个答案:

答案 0 :(得分:7)

您是否尝试创建包含AnalogTvGraphFileElement元素的用户控件,然后在模板中使用该用户控件,而不是将此元素直接放入模板中?

如果此处的问题是由模板系统进行的,并且首先设置了取消设置的属性,那么将元素移动到用户控件应该会有所帮助,因为不再从模板中设置属性。 / p>

至于为什么你首先看到这个行为,据我所知,Unloaded事件的相对顺序和通过模板设置的属性的丢失没有记录,所以你不应该依赖任何特定的顺序。您将丢失属性值的事实已记录在案。 (或者至少,它是从文档隐含的。)WPF属性系统将模板中的本地值视为与模板之外的普通本地值不同的东西。请参阅dependency property precedence上的此MSDN页面 - 4b表示模板中的本地属性集与本地属性不同。 (区分似乎很奇怪,但应该可以通过在模板中设置属性值来设置属性值 - 类型4b - 然后在运行时,在模板的特定实例中找到元素并设置它的本地值来自代码 - 类型3.对于那种情况,你真的希望类型3本地值的优先级高于类型4b本地值。)

看起来很奇怪,当您考虑单个模板可能为多个实例提供值时,它可能更有意义。你只有一个可能影响任意数量元素的本地属性设置器。 (这意味着模板的简单心理模型作为构建可视树并在该树上设置属性的工厂将是错误的。它是一个构建可视树的工厂,但它不设置属性。属性系统简单地确保在没有任何更高优先级的属性值源的情况下,可视树中作为某事物模板的元素将从模板中的setter中获取值。)

该页面告诉您,一旦模板停止活动,类型4b属性将消失 - 模板不再是模板化父级的模板,因此该模板提供的任何本地值都不再符合候选条件属性的类型4b(或任何其他类型)的值。简而言之,一旦模板不再是某事物的模板,它就不再有任何业务为该事物或其中的任何事物提供价值。这意味着模板实例的可视化树进入一种奇怪的状态,在该状态下,它不再是任何模板,但尚未卸载。

当然,模板在相关可视化树完成卸载之前停止提供值似乎有用,但也许属性的值仅在元素卸载时才有意义一个奇怪的案例,而不是一个专门设计的案例。考虑到它,它们可能永远不应该是依赖属性 - 一旦从视觉树中卸载,几乎所有使DP有用的功能并不是很有意义。可以说,媒体工具包中的一个错误是UnloadedBehaviour首先是DP。

继承属性(类型10)比本地模板属性集(类型4b)更晚关闭是不一致的,但是我不确定在这里期望任何特定顺序是合理的。文档暗示一个订单或另一个订单并不明显,因此任何订单都是正确的......并且WPF似乎通过在一个场景中选择一个订单而在另一个场景中选择另一个订单来利用它。