WPF DataTemplate 在卸载时重置一些依赖属性
我有一个 DataTemplate
,其中包含一个媒体元素控件,该控件派生自 WPF 媒体工具包 库。 MediaElementBase 类提供两个属性,LoadedBehavior 和 UnloadedBehavior 允许用户指定加载/卸载元素时发生的情况。
我发现在 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
的更多信息,您可以查看 此问题以获取说明。
I have a DataTemplate
consisting of a media element control that is derived from MediaElementBase
from the WPF Media Kit library. The MediaElementBase class provides the two properties, LoadedBehavior
and UnloadedBehavior
that allow the user to specify what happens when the element is loaded/unloaded.
I am finding that when using this in a DataTemplate
(as follows), these properties get reset to their default value when the template is unloaded, but before the Unloaded
event is called, meaning only the default UnloadedBehavior
will ever execute:
<DataTemplate DataType="{x:Type Channels:AnalogChannel}">
<Controls:AnalogTvGraphFileElement
LoadedBehavior="Play"
UnloadedBehavior="Stop"
Channel="{Binding}" />
</DataTemplate>
This doesn't occur when the control is simply an element on a page and Unloaded
occurs through a normal navigate-away event.
Debugging the DependencyPropertyChanged EventHandler reveals that an internal method System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode
(in PresentationFramework.dll) checks if a DependencyProperty
is potentially inherited, and if it isn't, invalidates it. Sure enough, changing the property metadata for the LoadedBehavior
/ UnloadedBehavior
to add FrameworkPropertyMetadataOptions.Inherits
stops this property from being reset when the template changes.
Does anyone know why this occurs? I can add the Inherits
flag as a workaround, as this element has no child elements that would be affected by this, but I'd like to know why / whether it is the right thing to do.
If you're after more information about what I'm doing and why I'm changing DataTemplates
you can view this question for a description.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您是否尝试过创建包含
AnalogTvGraphFileElement
元素的用户控件,然后在模板中使用该用户控件,而不是直接将此元素放入模板中?如果这里的问题是由模板系统运行并取消设置它首先设置的属性引起的,那么将元素移动到用户控件中应该会有所帮助,因为将不再从模板设置属性。
至于为什么您首先会看到这种行为,据我所知,未记录 Unloaded 事件的相对顺序以及通过模板设置的属性丢失,因此您不应依赖于任何特定顺序。您将丢失属性值的事实已被记录。 (或者至少,它是在文档中隐含的。)WPF 属性系统将模板中的本地值视为与模板外部的正常本地值不同的事物。请参阅此 MSDN 页面,了解依赖属性优先级 - 4b 表示模板中的本地属性集不是与本地属性相同。 (区分似乎很奇怪,但是应该可以通过在模板中设置属性值(类型 4b)来设置两个源的属性值,然后在运行时在模板的特定实例中查找元素并设置它的本地值来自代码 - 类型 3。对于这种情况,您确实希望类型 3 本地值比类型 4b 本地值具有更高的优先级。)
虽然这看起来很奇怪,但当您考虑到单个模板可以为多个实例提供值。您只有一个可以影响任意数量元素的本地属性设置器。 (这意味着将模板视为构建可视化树并在该树上设置属性的工厂的简单心智模型是错误的。它是构建可视化树的工厂,但不设置属性。属性系统只是确保在没有任何更高优先级的属性值源的情况下,作为某些内容模板的可视化树中的元素将从模板中的 setter 中获取值。)
该页面只是告诉您类型 4b 属性将消失一旦模板停止活动 - 该模板不再是模板化父级的模板,因此该模板提供的任何本地值不再有资格作为该属性的类型 4b(或任何其他类型)的候选值。简而言之,一旦模板不再是某事物的模板,它就不再有任何业务为该事物或其中的任何内容提供价值。这意味着模板实例的可视化树进入了一种奇怪的边缘状态,其中它不再是任何内容的模板,但尚未卸载。
当然,在相关可视化树完成卸载之前,模板停止提供值似乎没有什么用处,但也许只有在元素卸载时其值才有意义的属性有点奇怪案例,而不是专门设计的案例。考虑一下,它们可能永远不应该是依赖属性 - 一旦从可视化树中卸载,几乎所有使 DP 有用的功能都没有多大意义。因此可以说,UnloadedBehaviour 首先是 DP,这是 Media Kit 中的一个错误。
继承属性(类型 10)晚于本地模板属性集(类型 4b)关闭是不一致的,但我不确定在这里期望任何特定顺序是否合理。文档暗示一个顺序或另一个顺序并不明显,因此任一顺序都是正确的...并且 WPF 似乎通过在一个场景中选择一个顺序而在另一个场景中选择另一个顺序来利用这一点。
Rather than putting this element directly into the template, have you tried creating a user control that contains your
AnalogTvGraphFileElement
element, and then using that user control in your template?If the problem here is being caused by the template systems going and unsetting properties that it set in the first place, moving your element into a user control should help, because the properties would no longer be being set from the template.
As for why you're seeing the behaviour in the first place, as far as I can tell the relative ordering of the Unloaded event and the loss of properties set via the template is not documented, so you shouldn't depend on any particular ordering. The fact that you will lose the property values is documented. (Or at least, it's implicit from the docs.) The WPF property system treats local values in a template as being a different sort of thing than normal local values outside of a template. See this MSDN page on dependency property precedence - 4b indicates that local property sets in the template are not the same thing as local properties. (It seems odd to make a distinction, but it should be possible to set property values from both sources by setting them in the template - type 4b - and then at runtime, going and finding the element in a particular instance of the template and setting its local value from code - type 3. And for that scenario you really would want type 3 local values to have higher precedence than type 4b local values.)
Weird as that seems, it might make more sense when you consider that a single template may be providing values for multiple instances. You've got just one local property setter that could affect any number of elements. (This means that a simple mental model of a template as being a factory that builds a visual tree and sets properties on that tree would be wrong. It's a factory that builds a visual tree, but it doesn't set properties. The property system simply ensures that in the absence of any higher-precedence property value sources, the elements in a visual tree that's the template for something will pick up values from setters in the template.)
That page does just about tell you that type 4b properties will disappear once the template ceases to be active - the template is no longer the template for the templated parent, and so any local values provided by that template no longer qualify as candidate values of type 4b (or any other type for that matter) for the property. In short, once the template ceases to be the template for something, it ceases to have any business providing values for that thing or anything in it. This implies that the visual tree for an instance of a template enters a weird limbo state in which it is no longer the template for anything but hasn't yet unloaded.
Of course, it doesn't seem useful for the template to stop providing values before the relevant visual tree has finished unloading, but perhaps properties whose value is only significant when an element unloads are a bit of a weird case, and not one that was specifically designed for. Thinking about it, they probably shouldn't ever be dependency properties - almost all the features that make DPs useful don't really mean much once you're unloaded from the visual tree. So arguably, it's a bug in the Media Kit that UnloadedBehaviour is a DP in the first place.
It's inconsistent that inherited properties (type 10) shut down later than local template property sets (type 4b), but then I'm not sure it's reasonable to expect any particular order here. It's not obvious that the documentation implies one order or the other, so either order is correct...and WPF appears to make use of that by picking one order in one scenario and the other in another scenario.