Reputation: 1274
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
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
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