user989056
user989056

Reputation: 1274

Altering a ControlTemplate from code behind

Application:

Hello, I am dynamically adding custom controls to a WPF application. The control is a custom slider. I have created a ControlTemplate in the XAML file which I want to use as the template for these dynamically created controls. I am currently applying the template by using:

newControl.Template = (ControlTemplate)parent.Resources["nameOfTheControlTemplate"];

This currently works OK (i.e. compiles, runs, and applys the template).

The template looks like this: ( Sorry for wall of text )

<ControlTemplate x:Key="errorRangeSliderRight" TargetType="{x:Type Slider}">
<Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Track x:Name="PART_Track" Grid.Row="1">
            <Track.Thumb>
                <Thumb x:Name="Thumb" HorizontalContentAlignment="Right" Width="7">
                    <Thumb.Template>
                        <ControlTemplate TargetType="Thumb">
                            <Path x:Name="nameOfPath" Stroke="Black" StrokeThickness="0" Fill="Red">
                                <Path.Data>
                                    <GeometryGroup FillRule="NonZero">
                                        <PathGeometry>
                                            <PathGeometry.Figures>
                                                <PathFigure IsClosed="True" StartPoint="7,150">
                                                    <PathFigure.Segments>
                                                        <PathSegmentCollection>
                                                            <LineSegment Point="5,150" />
                                                            <LineSegment Point="5,0" />
                                                            <LineSegment Point="7,0" />
                                                        </PathSegmentCollection>
                                                    </PathFigure.Segments>
                                                </PathFigure>
                                                <PathFigure IsClosed="True" StartPoint="0,75">
                                                    <PathFigure.Segments>
                                                        <PathSegmentCollection>
                                                            <LineSegment Point="7,70" />
                                                            <LineSegment Point="7,80" />
                                                            <LineSegment Point="0,75" />
                                                        </PathSegmentCollection>
                                                    </PathFigure.Segments>
                                                </PathFigure>
                                            </PathGeometry.Figures>
                                        </PathGeometry>
                                    </GeometryGroup>
                                </Path.Data>
                            </Path>
                        </ControlTemplate>
                    </Thumb.Template>
                </Thumb>
            </Track.Thumb>
        </Track>
    </Grid>
</Border>
</ControlTemplate>

Reasoning:

The reason why I chose to define the control template in the XAML rather than dynamically create a template using millions of lines of code using FrameworkElementFactory's is because it is easier, cleaner, and easier to maintain/read.

What I want:

I would like to make slight alterations to this control template ( only the Fill color of a Path object ) within the ControlTemplate. If it is possible I would like to get a reference to the ControlTemplate object.

What I have tried:

I have tried calling a FindName("nameOfPath") on the Template, it returns a null object.

Object o = newControl.Template.FindName("nameOfPath",newControl);

I have tried creating the ContentTemlpate using lots of FrameworkElementFactory instances and building the ControlTemplate that way, this was unsuccessful ( the ControlTemplate object is quite complex and has many child elements).

Upvotes: 1

Views: 5916

Answers (2)

brunnerh
brunnerh

Reputation: 184306

I would strongly discourage tampering with templates (or rather controls instantiated from templates) at runtime in this way, it is bad design.

What you want to do instead is using properties. For example bind the Path.Fill to the Foreground? If you think that is not semantically fitting enough you would be better off inheriting from Slider and creating the property you need rather than messing with the template at runtime.

In your case you have nested templates so you have to forward the TemplateBinding or do something with a RelativeSource binding, it is up to you. e.g. using forwarding:

<!-- ... -->
<Thumb x:Name="Thumb" HorizontalContentAlignment="Right" Width="7"
            Background="{TemplateBinding Foreground}">
        <Thumb.Template>
            <ControlTemplate TargetType="Thumb">
                <Path x:Name="nameOfPath" Stroke="Black" StrokeThickness="0"
                      Fill="{TemplateBinding Background}">

This binds the Path.Fill to the Thumb.Background (which it is) and the Thumb.Background is bound to the Slider.Foreground which seems reasonable as well. Now you just need to set the Foreground on the Slider to set the Fill of the path, nice, is it not?


As an aside: You should keep the idea of what you want abstract.

I would like to make slight alterations to this control template ...

That is in fact not what you want but a consequence that you think is needed to get you what you want. You put what you really want into parenthesis: "[alterations to] the Fill color of a Path object"

Upvotes: 2

Rachel
Rachel

Reputation: 132548

You probably don't want to alter your Template in code-behind, because that would alter every control that uses that Template.

Instead, once your control has been Rendered, you can navigate it's visual tree to find the specific elements that need to be altered and change them. The Loaded event gets run after all Render events get processed, so you should be able to adjust controls in a control's template during any Loaded event.

I wrote a set of VisualTreeHelpers which I use quite often to find controls within WPF's visual tree. They can be used like this:

Path path = VisualTreeHelpers.FindChild<Path>(newControl, "nameOfPath");

Upvotes: 1

Related Questions