从后面的代码改变ControlTemplate

时间:2011-11-29 15:27:49

标签: c# wpf xaml wpf-controls controltemplate

应用

您好,我正在为WPF应用程序动态添加自定义控件。该控件是一个自定义滑块。我在XAML文件中创建了一个ControlTemplate,我想将其用作这些动态创建的控件的模板。我目前正在使用以下方法应用模板:

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

目前可以正常工作(即编译,运行和应用模板)。

模板如下所示:(抱歉,文字墙)

<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>

推理:

我选择在XAML中定义控件模板而不是使用FrameworkElementFactory使用数百万行代码动态创建模板的原因是因为它更容易,更清晰,更易于维护/读取。

我想要的是什么:

我想在ControlTemplate中对此控件模板(仅限Path对象的填充颜色)进行轻微更改。如果可能的话,我想获得对ControlTemplate对象的引用。

我尝试了什么:

我尝试在模板上调用FindName(“nameOfPath”),它返回一个空对象。

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

我尝试使用大量的FrameworkElementFactory实例创建ContentTemlpate并以这种方式构建ControlTemplate,这是不成功的(ControlTemplate对象非常复杂并且有许多子元素)。

2 个答案:

答案 0 :(得分:2)

我会强烈阻止在运行时以这种方式篡改模板(或者更确切地说是从模板实例化的控件),这是糟糕的设计。

您要做的是使用属性。例如,将Path.Fill绑定到Foreground?如果您认为这在语义上不够合适,那么最好继承Slider并创建所需的属性,而不是在运行时弄乱模板。

在您的情况下,您拥有嵌套模板,因此您必须转发TemplateBinding或使用RelativeSource绑定执行某些操作,这取决于您。例如使用转发:

<!-- ... -->
<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}">

这会将Path.Fill绑定到Thumb.Background(它是),Thumb.Background绑定到Slider.Foreground,这似乎也是合理的。现在你只需要在Foreground上设置Slider来设置路径的Fill,不错,不是吗?


顺便说一句:你应该保留你想要抽象的想法。

  

我想对此控件模板稍作修改......

事实上,这不是你想要的,而是你认为需要的结果才能得到你想要的东西。您将真正想要的内容放入括号中:“[更改为Path对象的填充颜色”

答案 1 :(得分:1)

您可能不希望在代码隐藏中更改模板,因为这会改变使用该模板的每个控件。

相反,一旦您的控件已经渲染,您可以导航它的可视树以找到需要更改的特定元素并更改它们。 Loaded事件在处理完所有Render事件后运行,因此您应该能够在任何Loaded事件期间调整控件模板中的控件。

我写了一组VisualTreeHelpers,我经常使用它来查找WPF视觉树中的控件。它们可以像这样使用:

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