如何在Path.Data中使用TemplateBinding?

How to use TemplateBinding in Path.Data?

如何将 Path.Data 内的 属性 绑定到 TemplateBinding?我注意到在以下示例中,属性 SegmentColorStrokeThickness 已正确设置和更新,但 属性 TargetPoint 未正确设置和更新。进一步的测试似乎证实该问题似乎与 属性 嵌套在 Path.Data 的元素中有关。以下代码试图简化我在为自定义控件创建模板时所面临的上下文。

C#:

public class TestProgressBar : ProgressBar
{
    public Brush SegmentColor
    {
        get { return (Brush)GetValue(SegmentColorProperty); }
        set { SetValue(SegmentColorProperty, value); }
    }

    public double StrokeThickness
    {
        get { return (double)GetValue(StrokeThicknessProperty); }
        set { SetValue(StrokeThicknessProperty, value); }
    }

    public Point TargetPoint
    {
        get { return (Point)GetValue(TargetPointProperty); }
        set { SetValue(TargetPointProperty, value); }
    }

    public static readonly DependencyProperty StrokeThicknessProperty =
        DependencyProperty.Register(nameof(StrokeThickness), typeof(double), typeof(TestProgressBar), new PropertyMetadata());

    public static readonly DependencyProperty SegmentColorProperty =
        DependencyProperty.Register(nameof(SegmentColor), typeof(Brush), typeof(TestProgressBar), new PropertyMetadata(new SolidColorBrush(Colors.Red)));

    public static readonly DependencyProperty TargetPointProperty =
        DependencyProperty.Register(nameof(TargetPoint), typeof(Point), typeof(TestProgressBar), new PropertyMetadata());
}

Xaml:

<c:TestProgressBar StrokeThickness="15"
                   TargetPoint="100,0">
    <c:TestProgressBar.Style>
        <Style TargetType="{x:Type c:TestProgressBar}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type c:TestProgressBar}">
                        <Grid>
                            <Path
                                  Stroke="{TemplateBinding SegmentColor}"
                                  StrokeThickness="{TemplateBinding StrokeThickness}"
                                  Width="100" Height="100">
                                <Path.Data>
                                    <PathGeometry>
                                        <PathFigure>
                                            <LineSegment Point="{TemplateBinding TargetPoint}"/>
                                        </PathFigure>
                                    </PathGeometry>
                                </Path.Data>
                            </Path>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </c:TestProgressBar.Style>
</c:TestProgressBar>

似乎是在 Path.Data 中使用 TemplateBindings 导致了这个问题。用 TemplatedParent Binding 替换它可以解决问题:

<LineSegment Point="{Binding TargetPoint, RelativeSource={RelativeSource TemplatedParent}}"/>

虽然我不能完全解释这是为什么。在我的原始代码中,我使用的是只读依赖属性,因此该问题不能与 TwoWay 绑定问题相关联(因为我很难将其与 TemplateBinding 联系起来)。

我知道使用 TemplatedParent 的绑定是 runtime-evaluated,而不是使用 TemplateBinding 的 compile-time,所以也许沿着这些思路修复了绑定。

问题是关于 TemplateBinding 的一个鲜为人知的细节,它们 do not work on properties Freezables. In fact, a LineSegment 间接派生自 Freezable,如您在其继承层次结构中所见。

Object -> DispatcherObject -> DependencyObject -> Freezable -> Animatable -> PathSegment -> LineSegment

然而,Path 没有,这就是为什么 TemplateBinding 对其属性起作用的原因。由于模板绑定只是 Binding 的优化 但受限 版本,您始终可以 use its binding syntax equivalent 没有任何限制并且也适用于 Freezables.

<LineSegment Point="{Binding TargetPoint, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>

注意,这里 Mode 设置为 OneWay,以指出 TemplateBinding 始终是 one-way,这相当于 Binding,但绑定更强大,支持任何绑定模式。

I am using readonly dependency properties, so the issue can't be linked to TwoWay binding problems (as I've hard can be the case with TemplateBinding).

依赖项 属性 声明必须是 read-only,但这不会使依赖项 属性 read-only 本身,这是通过声明依赖项 [=46] 来完成的=] 键并使用 RegisterReadOnly 方法。