从代码隐藏更改 ControlTemplate

发布于 2024-12-18 20:25:43 字数 4152 浏览 1 评论 0原文

应用程序:

您好,我正在向 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 实例创建 ContentTemplate 并以这种方式构建 ControlTemplate,但这是不成功的( ControlTemplate 对象非常复杂并且具有许多子元素)。

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青衫负雪 2024-12-25 20:25:43

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

相反,您想要做的是使用属性。例如将 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 这看起来也很合理。现在你只需要设置Slider上的Foreground来设置路径的Fill,很好,不是吗?


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

我想对此控件模板进行一些细微的修改...

这实际上不是您想要的,而是您认为需要得到您想要的结果。您将真正想要的内容放入括号中:“[更改]路径对象的填充颜色”

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"

做个少女永远怀春 2024-12-25 20:25:43

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

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

我写了一组 VisualTreeHelpers 我相当使用它通常在 WPF 的可视化树中查找控件。它们可以这样使用:

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

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");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文