【发布时间】:2011-01-29 01:29:50
【问题描述】:
我有一个 ClockFace UserControl,它公开了许多属性以使用户能够对其进行样式设置。时钟有两个 Ellipse 对象作为边框;外边框和内边框。
<Ellipse Name="OuterBorder" Panel.ZIndex="5" StrokeThickness="{Binding BorderOuterThickness}" Stroke="{Binding BorderOuteBrush}" />
<Ellipse Name="InnerBorder" Panel.ZIndex="6" StrokeThickness="{Binding BorderInnerThickness}" Margin="{Binding StrokeThickness, ElementName=OuterBorder}" Stroke="{Binding BorderInnerBrush}">
public static readonly DependencyProperty BorderInnerBrushProperty = DependencyProperty.Register("BorderInnerBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(new LinearGradientBrush(Color.FromRgb(118, 57, 57), Color.FromRgb(226, 185, 185), new Point(0.5, 0), new Point(0.5, 1))));
public Brush BorderInnerBrush
{
get { return (Brush)GetValue(BorderInnerBrushProperty); }
set { SetValue(BorderInnerBrushProperty, value); }
}
public static readonly DependencyProperty BorderOuterBrushProperty = DependencyProperty.Register("BorderOuterBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(new LinearGradientBrush(Color.FromRgb(226, 185, 185), Color.FromRgb(118, 57, 57), new Point(0.5, 0), new Point(0.5, 1))));
public Brush BorderOuterBrush
{
get { return (Brush)GetValue(BorderOuterBrushProperty); }
set { SetValue(BorderOuterBrushProperty, value); }
}
这些可以在样式中设置,并在样式切换时正确更新。我认为我会很聪明,并添加一个名为 BorderBrush 的快捷方式属性,该属性将自身传递给 BorderOuterBrush 属性,然后将其自身的副本传递给其渐变反转到 BorderInnerBrush 属性。为了在初始化后(通过切换样式)设置属性时启用此代码,我必须实现一个调用 SetBorderBrushes 方法的 PropertyChangedCallback 方法。
public new static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(OnBorderBrushChanged));
private static void OnBorderBrushChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
((ClockFace)dependencyObject).SetBorderBrushes((Brush)e.NewValue);
}
public new Brush BorderBrush
{
get { return (Brush)GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
}
private void SetBorderBrushes(Brush brush)
{
if (brush != null)
{
BorderOuterBrush = brush;
Brush innerBrush = BorderOuterBrush.Clone();
if (brush.GetType() == typeof(LinearGradientBrush) || brush.GetType() == typeof(RadialGradientBrush))
{
foreach (GradientStop gradientStop in ((GradientBrush)innerBrush).GradientStops)
{
gradientStop.Offset = 1 - gradientStop.Offset;
}
}
BorderInnerBrush = innerBrush;
}
}
当 BorderBrush 属性设置为样式时,一切正常……直到我在运行时切换样式。现在最奇怪的事情发生了……让我解释一下。
我有四种预设样式,它们各自都可以正常工作。其中三个使用两个 BorderInnerBrush 和 BorderOuterBrush 属性,一个使用快捷方式 BorderBrush 属性。我可以使用 ContextMenu 和后面的一些代码在样式之间切换,这些代码从 Resources 访问 xaml 样式并将它们设置为 ClockFace 对象的 Style 属性。
我可以在不使用快捷方式属性的三种样式之间无休止地切换而不会出现问题。我也可以切换到使用快捷方式属性的样式,它看起来很好。这就是奇怪的开始。
切换到使用 BorderBrush 属性的样式后,BorderInnerBrush 和 BorderOuterBrush 属性将停止工作。以各种样式设置的预设 Brush 对象不再设置在两个 Ellipse 对象上。我在内部和外部边框属性中插入了一些 PropertyChangedCallback 方法,看看发生了什么。
当我第一次运行应用程序时,我可以毫无问题地切换到不使用快捷方式属性的三种样式。我在所有三个边框画笔属性的 PropertyChangedCallback 方法上都设置了断点并调试了程序。当切换到这三种样式中的每一种时,内边框属性和外边框属性的回调方法的断点都如您所愿。当切换到使用 BorderBrush 属性的样式时,它的回调方法的断点被命中。 SetBorderBrushes 方法设置了另外两个边框 Brush 对象,因此内边框属性和外边框属性的回调方法的断点会如您所愿地命中。
再次,这是奇怪的部分。在此之后切换到其他三种样式中的任何一种时,内边框属性和外边框属性的回调方法的断点都不再被命中。相反,附加到 BorderBrush 属性的回调方法中的断点被击中,并且 e.NewValue 的值为 null。由于在 SetBorderBrushes 方法中忽略 null 值,因此不会再命中断点。
经过进一步调查,我发现 e.NewValue 为空,因为 BorderBrushProperty DependencyProperty 上没有设置默认值。事实上,在声明中添加一个默认的 Brush 对象后,这就是将在回调方法中的 e.NewValue 中传递的 Brush。虽然这两个内边框属性和外边框属性的回调方法中的断点在这之后会被击中,但这只是因为它们是在SetBorderBrushes方法中设置的。在使用一次 BorderBrush 属性后,样式中的 BorderInnerBrush 和 BorderOuterBrush 对象中设置的 Brush 对象永远不会传递给这些属性。
最后要注意的一点是,正如在样式中没有显式设置值时设置 BorderBrushProperty 属性的默认值一样,内边框和外边框 DependencyProperty 对象的默认值也会在它们的值被设置时设置未在样式中显式设置,但仅当未在样式中设置 BorderBrush 属性时。
我已经被这个问题困扰了好几天,虽然一个简单的解决方案是删除快捷方式属性,但我宁愿找出发生了什么并修复它。我希望我已经提供了足够的信息,让一些明亮的火花能够解决这个问题,所以,如果你有任何想法或问题,请分享。非常感谢。
更新>>
按照 Rick 的建议,我创建了另一对 Brush 属性,并通过附加到其他三个 Brush 属性的回调方法设置它们。使用此设置,我可以在所有样式之间切换而不会出现任何视觉问题……或者我是这么认为的。
我现在可以从使用 BorderBrush(快捷方式)属性的样式切换到设置两个 BorderInner 和 BorderOuter 属性并且边框正确更新的样式,所以感谢 Rick 让我更进一步。从设置 BorderBrush 属性的样式切换到未显式设置任何边框 Brush 的样式时,我仍然遇到问题。
因此,我仍然有同样的问题,即 BorderInnerBrush 和 BorderOuterBrush 属性在从后面的代码设置后不能直接工作。新的是,如果我然后切换到设置两个 Brush 属性的样式,这似乎再次“唤醒它们”......如果我然后切换到不设置边框 Brush 的样式,则内部和外部属性设置它们默认值再次正确。
只有在使用从后面的代码中设置两个边框属性的样式之后,它们才会被设置为样式或使用它们的默认值。这太奇怪了……有人能解决吗?
更新 2 >>>
首先,非常感谢您抽出宝贵时间和示例 Rick。在将 Rick 的示例代码复制并粘贴到一个新项目中之后,我必须在它运行之前进行一些更改......也许是因为我没有 Expression Blend 4?我安装了 SDK 并添加了对上述 dll 的引用,但必须改用以下 XML 命名空间声明:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
我还必须将每个 ChangePropertyAction 声明稍微更改为:
<ei:ChangePropertyAction TargetName="clock" PropertyName="Style" Value="{StaticResource styleBrush}"/>
然后,它对我来说工作得很好,并且很好地代表了我实际 ClockFace 控件的那一部分,但有两个例外。首先是我的样式是通过事件处理程序从代码后面切换的——这显然不会导致我的问题。第二个区别是我在我的 ActualOuterBrush 和 ActualInnerBrush 版本上设置了默认的 Brush 值,因此控件的用户不必提供边框画笔。
所以我的最后一个问题是,在 Style 中设置 BorderBrush 属性后,没有明确设置边框属性的样式中没有设置默认的内边框和外边框画笔。请记住,在此之前确实设置了默认值。因此,我尝试了 Rick 的示例并添加了一些默认值:
private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Teal));
private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Salmon));
现在这个项目有一个类似的问题......默认值永远不会重置。我对这种行为感到很困惑。
我在这个问题上花了很长时间,虽然我从 Rick 那里学到了很多有用的东西,但我仍然对默认值有这个问题。我决定最简单的做法是删除快捷方式 BorderBrush 属性。 Rick 帮了大忙,我将他的答案评为正确答案,但如果有人阅读本文可以帮助了解默认值,我将不胜感激。
【问题讨论】:
标签: wpf callback dependency-properties