We are trying to get what the value of a DependencyProperty would be if it weren’t locally set in the XAML. For instance, take this XAML…
<StackPanel Orientation="Vertical"
TextElement.FontSize="24">
<TextBlock Text="What size am I?"
FontSize="{Binding FontSize, RelativeSource={RelativeSource Self}, Converter={StaticResource PercentageConverter}, ConverterParameter=0.75}" />
</StackPanel>
This of course doesn’t work. What I’ve done instead is this…
<TextBlock Text="What size am I?"
FontSize="{Binding (TextElement.FontSize), RelativeSource={RelativeSource AncestorType=FrameworkElement}, Converter={StaticResource PercentageConverter}, ConverterParameter=0.5}" />
…which checks the parent’s value for the inherited FontSize property but that only works because FontSize does support inheritance. I’m looking for a solution for any DependencyProperty, not just ones that can be inherited.
Update
I changed the title of the question to be a little more generic as to what would be helpful to us. Specifically we’d love to be able to say ‘For this specific instance of a DependencyObject, what would the value of a DependencyProperty be for each of the precedence levels?’ When we query a DP, we of course get what the value after all precedence is applied. We’d love to say ‘What would it be one or two levels higher?’, etc.
A generic and robust solution should take into account the full rule set for DependencyProperty value precedence. I don’t think that it is safe to say that if a value is not set locally, then its value would be the default value – the value instead could then be provided by a style, a trigger, etc.
I would suggest taking a look at the class
DependencyPropertyHelper. This class contains a method calledGetValueSourcewhich when given aDependencyObjectand aDependencyPropertywill return a BaseValueSource result that tells you where the value for the property is coming from (style, style trigger, inherited, local, etc).That being said, I think it would not be too hard to write an attached behaviour which you could use in XAML to return the “non-local” value for a specified
DependencyProperty. This behaviour would check if the value source is local, and if it is clone the element, add it to the logical tree, and clear the local value. It would then read the value from the specifiedDependencyPropertyon the clone, and set a read-only attachedDependencyPropertyto this value. This way you are letting the WPF property engine do the work for you.Hope this helps!
Edit:
Here is a proof of concept for the attached behaviour. The behaviour has 2 properties:
DependencyPropertywho’s value youwish to inspect.
DependencyProperty.In XAML you bind to these 2 properties (setting one, reading the other):
The example above will correctly show the values: 30, 35, 25, and 11. A couple of limitations in the code below is that the
DependencyObjectwhich we are inspecting must be a child of aPanel, and that currently only theStyleproperty is copied over, but really all properties should be cloned since triggers in the style could use any property to change the value aDependencyProperty. Food for thought I guess …Attached Behaviour:
Edit 2
To elaborate on this approach. There is no chain of values exposed by WPF which we can inspect (or at least that I know of). The code above attempts to discover what the value would be if it was not set locally (from whatever value source: inherited, styles, triggers, etc). When it discovers this value it then populates the
InspectedValueproperty with the result, which then can be read from.A first attempt at this would be to:
DependencyObject.ClearValue)This approach fails – in that during step 2 your property will not pick up values from default styles (for example) since the style has already been applied, and simply removing a local value from a random
DependencyProperydoes not trigger a re-application.Another approach would be in step 2 above – remove the element from the logical tree, and then add it back (since when elements are added to the tree, WPF goes through all possible value sources to determine the value of each DP). This too fails for the same reason – you are not guaranteed that styles will be re-applied.
The attached behaviour above tries a third approach – clone an element but make sure the the property in question is not set on the clone, and then add it to the logical tree. At this point WPF will then process the element as it would have with the original element and apply a value to the DP (inherited, triggers, style, etc). We can then read its value, and store it away. As demonstrated, this approach works for common use-cases, but is not perfect since if the non-local value was coming from a
Triggerusing a read-only property likeIsMouseOverit would not pick it up (and a deep copy of the original object state would not fix this).