如果未在XAML中本地设置,我们试图获得DependencyProperty
的值。例如,拿这个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时,我们当然得到应用所有优先级后的值。我们很乐意说'它会更高一两级?'等等。
答案 0 :(得分:2)
通用且强大的解决方案应考虑DependencyProperty value precedence的完整规则集。我不认为如果没有在本地设置一个值,那么它的值将是默认值 - 然后可以通过样式,触发器等提供该值,这是不安全的。
我建议你看看班级DependencyPropertyHelper
。此类包含一个名为GetValueSource
的方法,当给定DependencyObject
和DependencyProperty
时,将返回BaseValueSource结果,该结果会告诉您属性的值来自何处(样式) ,样式触发器,继承,本地等)。
话虽如此,我认为编写一个可以在XAML中用于返回指定DependencyProperty
的“非本地”值的附加行为并不会太难。此行为将检查值源是否为本地,如果它是克隆元素,则将其添加到逻辑树,并清除本地值。然后,它将从克隆上的指定DependencyProperty
读取值,并将只读的附加DependencyProperty
设置为此值。这样,您就可以让WPF属性引擎为您完成工作。
希望这有帮助!
修改强>
以下是附加行为的概念证明。该行为有2个属性:
DependencyProperty
希望检查。DependencyProperty
的计算非本地值。在XAML中,你绑定到这两个属性(设置一个,读另一个):
<StackPanel>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="30" />
</Style>
<Style x:Key="NamedStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="35" />
</Style>
</StackPanel.Resources>
<!-- Without local value - uses default style (30)-->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
<!-- Without local value - uses named style (35)-->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Style="{StaticResource NamedStyle}"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
</StackPanel>
<StackPanel TextElement.FontSize="25">
<!-- Without local value - uses inherited value (25) -->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
</StackPanel>
<!-- Without local value - uses default font size (11) -->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
</StackPanel>
上面的示例将正确显示值:30,35,25和11.以下代码中的一些限制是我们正在检查的DependencyObject
必须是{{1}的子项当前只复制Panel
属性,但实际上应该克隆所有属性,因为样式中的触发器可以使用任何属性来更改值Style
。值得深思的是我猜...
附加行为:
DependencyProperty
修改2
详细说明这种方法。 WPF没有公开任何价值链,我们可以检查(或者至少我知道)。上面的代码试图发现如果没有在本地设置它的值(从任何值源:继承,样式,触发器等)。当它发现这个值时,它会用结果填充public static class DependencyPropertyValueHelper
{
private static bool _isUpdating;
#region InspectedValue Read-only Attached Dependency Property
public static object GetInspectedValue(DependencyObject d)
{
return d.GetValue(InspectedValueProperty);
}
private static readonly DependencyPropertyKey InspectedValuePropertyKey =
DependencyProperty.RegisterAttachedReadOnly("InspectedValue", typeof (object),
typeof (DependencyPropertyValueHelper),
new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty InspectedValueProperty = InspectedValuePropertyKey.DependencyProperty;
#endregion
#region PropertyToInspect Attached Dependency Property
public static void SetPropertyToInspectValue(DependencyObject d, DependencyProperty dependencyProperty)
{
d.SetValue(PropertyToInspectValueProperty, dependencyProperty);
}
public static DependencyProperty GetPropertyToInspectValue(DependencyObject d)
{
return (DependencyProperty)d.GetValue(PropertyToInspectValueProperty);
}
public static readonly DependencyProperty PropertyToInspectValueProperty =
DependencyProperty.RegisterAttached("PropertyToInspectValue", typeof (DependencyProperty),
typeof (DependencyPropertyValueHelper),
new FrameworkPropertyMetadata(OnPropertyToInspectValuePropertyChanged));
#endregion
#region Private ValueChanged Attached Dependency Property
public static readonly DependencyProperty ValueChangedProperty =
DependencyProperty.RegisterAttached("ValueChanged", typeof(object),
typeof(DependencyPropertyValueHelper),
new FrameworkPropertyMetadata(OnValuePropertyChanged));
#endregion
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!_isUpdating)
DetermineNonLocalValue(d, GetPropertyToInspectValue(d));
}
private static void OnPropertyToInspectValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dependencyProperty = (DependencyProperty) e.NewValue;
DetermineNonLocalValue(d, dependencyProperty);
var binding = new Binding
{
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
Path = new PropertyPath(dependencyProperty.Name),
};
BindingOperations.SetBinding(d, ValueChangedProperty, binding);
}
private static void DetermineNonLocalValue(DependencyObject d, DependencyProperty dependencyProperty)
{
var element = d as FrameworkElement;
if (element == null)
return;
var valueSource = DependencyPropertyHelper.GetValueSource(element, dependencyProperty);
if (valueSource.BaseValueSource == BaseValueSource.Local)
{
_isUpdating = true;
var clonedDependencyObject = Activator.CreateInstance(element.GetType()) as FrameworkElement;
var parent = VisualTreeHelper.GetParent(element) as Panel;
// Currently only works if parent is a panel
if (parent != null)
{
// Copy any property which could impact the DP's value ...
// Probably check if this is a control and copy the ControlTemplate too ...
if (element.Style != null)
clonedDependencyObject.Style = element.Style;
parent.Children.Add(clonedDependencyObject);
// Let WPF provide us with the non-local value
element.SetValue(InspectedValuePropertyKey, clonedDependencyObject.GetValue(dependencyProperty));
parent.Children.Remove(clonedDependencyObject);
}
_isUpdating = false;
}
else
{
element.SetValue(InspectedValuePropertyKey, d.GetValue(dependencyProperty));
}
}
}
属性,然后可以从中读取。
首次尝试是:
InspectedValue
)这种方法失败了 - 因为在第2步中你的属性不会从默认样式中获取值(例如),因为样式已经应用了,只是从随机DependencyObject.ClearValue
中删除本地值不会触发重新申请。
另一种方法是在上面的步骤2中 - 从逻辑树中删除元素,然后将其添加回来(因为当元素添加到树中时,WPF会遍历所有可能的值源以确定每个DP的值) 。由于同样的原因,这也失败了 - 你不能保证重新应用样式。
上面的附加行为尝试第三种方法 - 克隆元素但确保未在克隆上设置有问题的属性,然后将其添加到逻辑树中。此时,WPF将处理元素,就像它对原始元素一样,并将值应用于DP(继承,触发器,样式等)。然后我们可以读取它的值,并将其存储起来。如上所示,这种方法适用于常见的用例,但并不完美,因为如果非本地值来自DependencyPropery
使用只有Trigger
这样的只读属性,它就不会捡起它(并且原始对象状态的深层副本不会解决此问题)。
答案 1 :(得分:0)
您应该使用最适合您需要的GetPropertyMetaData重载方法(应该是使用类型的方法),然后您可以使用... DefaultValue属性检索默认值。