是否可以获得 DependencyProperty 在每个优先级的值,而不仅仅是最终值?
我们试图获取 DependencyProperty
的值(如果未在 XAML 中本地设置)。例如,采用这个 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>
这当然行不通。我所做的是......
<TextBlock Text="What size am I?"
FontSize="{Binding (TextElement.FontSize), RelativeSource={RelativeSource AncestorType=FrameworkElement}, Converter={StaticResource PercentageConverter}, ConverterParameter=0.5}" />
它检查继承的 FontSize
属性的父级值,但这仅起作用,因为 FontSize
确实 支持继承。我正在寻找适用于任何 DependencyProperty 的解决方案,而不仅仅是可以继承的解决方案。
更新
我将问题的标题更改为更通用,以说明什么对我们有帮助。具体来说,我们希望能够说“对于 DependencyObject
的这个特定实例,每个优先级的 DependencyProperty
的值是多少?”当我们查询 DP 时,我们当然会得到应用所有优先级后的值。我们很乐意说“会高一到两级吗?”等等。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
通用且强大的解决方案应考虑DependencyProperty 值优先级。我不认为可以安全地说,如果一个值没有在本地设置,那么它的值将是默认值 - 该值可以由样式、触发器等提供。
我建议采取查看 DependencyPropertyHelper 类。此类包含一个名为
GetValueSource
的方法,当给定DependencyObject
和DependencyProperty
时,该方法将返回 BaseValueSource 结果告诉您属性的值来自何处(样式、样式触发器、继承、当地的, ETC)。话虽这么说,我认为编写一个附加行为并不太难,您可以在 XAML 中使用该行为来返回指定
DependencyProperty
的“非本地”值。此行为将检查值源是否是本地的,如果是克隆元素,则将其添加到逻辑树中,并清除本地值。然后,它会从克隆上指定的DependencyProperty
读取值,并将只读附加的DependencyProperty
设置为此值。这样,您就可以让 WPF 属性引擎为您完成工作。希望这有帮助!
编辑:
这是附加行为的概念证明。该行为有 2 个属性:
DependencyProperty
希望检查。
在 XAML 中,您绑定到这 2 个属性(设置一个,读取另一个):
上面的示例将正确显示值:30、35、25 和 11。下面代码中的一些限制是
DependencyObject我们正在检查的
必须是Panel
的子级,并且当前仅复制Style
属性,但实际上应该克隆所有属性,因为触发器在风格上可以使用任何属性来更改DependencyProperty
的值。我想深思熟虑...附加行为:
编辑2
详细说明这种方法。 WPF 没有公开任何我们可以检查的值链(或者至少据我所知)。上面的代码尝试发现如果未在本地设置该值(来自任何值源:继承、样式、触发器等),该值将会是什么。当它发现该值时,它会使用结果填充
InspectedValue
属性,然后可以从中读取结果。第一次尝试是:
此方法失败 - 在步骤 2 中,您的属性不会从默认样式中获取值(例如),因为样式已被应用,并且只需从随机
DependencyPropery
不会触发重新申请。另一种方法是上面的步骤 2 - 从逻辑树中删除元素,然后将其添加回来(因为当将元素添加到树中时,WPF 会遍历所有可能的值源来确定每个 DP 的值)。这也因同样的原因而失败 - 不能保证样式将被重新应用。
上面附加的行为尝试了第三种方法 - 克隆一个元素,但确保克隆上没有设置有问题的属性,然后将其添加到逻辑树中。此时,WPF 将像处理原始元素一样处理该元素,并将值应用于 DP(继承、触发器、样式等)。然后我们可以读取它的值并将其存储起来。如所示,此方法适用于常见用例,但并不完美,因为如果非本地值来自使用只读属性(如
IsMouseOver
)的Trigger
它不会拾取它(并且原始对象状态的深层副本无法解决此问题)。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 calledGetValueSource
which when given aDependencyObject
and aDependencyProperty
will 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 specifiedDependencyProperty
on the clone, and set a read-only attachedDependencyProperty
to 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:
DependencyProperty
who'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
DependencyObject
which we are inspecting must be a child of aPanel
, and that currently only theStyle
property 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
InspectedValue
property 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
DependencyPropery
does 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
Trigger
using a read-only property likeIsMouseOver
it would not pick it up (and a deep copy of the original object state would not fix this).您应该使用最适合您需要的 GetPropertyMetaData 的重载方法(应该是使用类型的方法),然后您可以使用... DefaultValue 属性检索默认值。
you should use the overloaded method of GetPropertyMetaData that suit best your need (that should be the one using types), and then you can retrieve the default value with... the DefaultValue property.