如何支持与触发器相关的视觉效果,同时保持控件的基本行为

本文关键字:控件 视觉 支持 何支持 触发器 | 更新日期: 2023-09-27 17:50:41

我有这样一个自定义控件:

<UserControl x:Class="MyNs.MyControl"
    <Border DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
        <Grid Margin="5,0,5,0">
            ...
            <TextBlock Text="{Binding Path=ControlData.DependencyPropertyOne}" />
            <TextBlock Text="{Binding Path=ControlData.DependencyPropertyTwo}" />
            <TextBlock Text="{Binding Path=ControlData.DependencyPropertyThree}" />
            ...
        </Grid>
    </Border>
</UserControl>

我这样使用它:

<UserControl x:Class="MyNs.ParentControl"
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type my:MyControl}">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <Border Background="Red" BorderThickness="6"></Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
        </Grid.Resources>
        <my:MyControl ControlData="{Binding CdOne}"/>
        <my:MyControl ControlData="{Binding CdTwo}"/>
        <my:MyControl ControlData="{Binding CdThree}"/>
        <my:MyControl ControlData="{Binding CdFour}"/>
        <my:MyControl ControlData="{Binding CdFive}"/>
        <my:MyControl ControlData="{Binding CdSix}"/>
    </Grid>
</UserControl>

但是当我用鼠标悬停时,内容被替换为红色背景(所有的TextBlock消失)。

那么如何允许宿主控件在保持控件的基本行为的同时改变一些依赖于触发器的视觉效果呢?

如何支持与触发器相关的视觉效果,同时保持控件的基本行为

您在IsMouseOver上有override the ControlTemplate of MyControl,因此包含您的内容的实际模板将不会显示。

理想情况下,这个触发器应该在MyControl中。但是,即使您想从外部执行,您也需要在边界内添加ContentPresenter,它将托管您的内容。

用下面的样式替换ControlTemplate:

<ControlTemplate>
   <Border Background="Red" BorderThickness="6">
       <ContentPresenter
            Content="{TemplateBinding ContentControl.Content}"
            ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
            ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
            HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
            VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
            SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
    </Border>
</ControlTemplate>

您的触发器需要在您的UserControl内,或者您的UserControl需要有依赖属性(例如;"BackgroundBrush"),外部的东西可以通过触发器设置,你的控制将在渲染自己时兑现。这就是TemplateBinding的用处所在。

实际上,任何使用你的控件的东西都不应该知道里面的模板,而应该只是设置公共属性。

如果你真的想把你的控件的逻辑从它的外观中分离出来,那么你应该创建一个合适的没有外观的Control,而不是UserControl,消费者可以根据需要提供他们自己的控件模板。这是标准WPF控件所采用的方法。