Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF DataTemplate resets some dependency properties when unloaded

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.

like image 750
jeffora Avatar asked Nov 24 '10 06:11

jeffora


1 Answers

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.

like image 126
Ian Griffiths Avatar answered Jan 10 '23 13:01

Ian Griffiths