Search code examples
c#wpfxamlcontroltemplatetemplatebinding

How to use TemplateBinding in Path.Data?


How can I bind a property inside Path.Data to a TemplateBinding? I noticed that in this following example, the properties SegmentColor and StrokeThickness are set and updated correctly, but not the property TargetPoint. Further testing seems to confirm the issue seems to be related to the property being nested in an element of Path.Data. The following code tries to simplify the context which I am facing while creating the template for a custom control.

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>

Solution

  • It seems that using TemplateBindings within the Path.Data causes the issue. Replacing it with a TemplatedParent Binding fixes the issue:

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

    I can't quite explain why this is, though. In my original code, 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).

    I know that Binding using TemplatedParent is runtime-evaluated, as opposed to compile-time for TemplateBinding, so perhaps something along those lines fixes the binding.