You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF:如何为继承外观的自定义按钮添加触发器并保留默认样式

问题解答

问题概述

基于Button封装了CustomButton,自带圆角边框等自定义ControlTemplate和依赖属性。现在想给它添加DataTrigger做视图验证,但直接在<controls:CustomButton.Triggers>中添加无效,必须重新定义Style——但新定义的<Style TargetType="{x:Type controls:CustomButton}">会覆盖CustomButton的默认样式。不想用BasedOn继承样式,也不想将控件套入容器(效率低下),询问是否能保留CustomButton默认样式的同时添加触发器。

相关代码

使用CustomButton的XAML

<controls:CustomButton
    Command="{Binding ValidateCommand}"
    Content="Validate"
    Color="{StaticResource PrimarySolidColorBrush}">

    <controls:CustomButton.Style>

        <!-- 此处会覆盖CustomButton的默认样式 -->
        <Style TargetType="{x:Type controls:CustomButton}">

            <Setter Property="IsEnabled" Value="True" />

            <Style.Triggers>

                <!-- 后续将添加更多触发器 -->
                <DataTrigger Binding="{Binding ElementName=Amount, Path=(Validation.HasError)}" Value="True">

                    <Setter Property="IsEnabled" Value="False" />

                </DataTrigger>

            </Style.Triggers>

        </Style>

    </controls:CustomButton.Style>

</controls:CustomButton>

CustomButton的XAML定义

<Button
    x:Class="Software.Desktop.Controls.CustomButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Software.Desktop.Controls"
    Padding="4"
    Cursor="Hand"
    FontSize="12"
    FontWeight="bold"
    Foreground="White">

    <Button.Resources>

        <!-- 样式 -->
        <Style BasedOn="{StaticResource TooltipStyle}" TargetType="{x:Type ToolTip}">

            <Setter Property="Foreground" Value="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type local:CustomButton}}}" />

        </Style>

        <!-- 控件模板 -->
        <ControlTemplate x:Key="ButtonControlTemplate" TargetType="{x:Type Button}">

            <Border
                x:Name="Bd"
                Background="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type local:CustomButton}}}"
                BorderBrush="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type local:CustomButton}}}"
                BorderThickness="0"
                CornerRadius="8">

                <ContentPresenter
                    Margin="{TemplateBinding Padding}"
                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                    Content="{TemplateBinding Content}"
                    ContentTemplate="{TemplateBinding ContentTemplate}" />

            </Border>

            <ControlTemplate.Triggers>

                <Trigger Property="IsMouseOver" Value="True">

                    <Setter TargetName="Bd" Property="Background" Value="White" />
                    <Setter TargetName="Bd" Property="BorderBrush" Value="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type local:CustomButton}}}" />
                    <Setter TargetName="Bd" Property="BorderThickness" Value="4" />
                    <Setter TargetName="Bd" Property="TextElement.Foreground" Value="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type local:CustomButton}}}" />

                </Trigger>

                <Trigger Property="IsEnabled" Value="False">

                    <Setter TargetName="Bd" Property="Background" Value="LightGray" />

                </Trigger>

            </ControlTemplate.Triggers>

        </ControlTemplate>

    </Button.Resources>

    <Button.Style>
        <Style TargetType="{x:Type Button}">

            <Setter Property="Template" Value="{StaticResource ButtonControlTemplate}" />

        </Style>
    </Button.Style>

</Button>

CustomButton的C#代码

namespace Software.Desktop.Controls
{
    /// <summary></summary>
    public partial class CustomButton : Button
    {
        #region 私有静态只读成员

        public static readonly DependencyProperty ColorProperty =
            DependencyProperty.Register(
                nameof(Color),
                typeof(Brush),
                typeof(CustomButton),
                new FrameworkPropertyMetadata(Application.Current.Resources["PrimarySolidColorBrush"]));

        #endregion

        #region 公共属性

        public Brush Color
        {
            get => GetValue(ColorProperty) as Brush;
            set => SetValue(ColorProperty, value);
        }

        #endregion

        #region 公共事件

        public event EventHandler Clicked;

        #endregion

        #region 公共构造函数

        /// <summary>默认构造函数</summary>
        public CustomButton()
        {
            Width = 64;
            Height = 32;

            InitializeComponent();

            Click += (sender, e) =>
            {
                Clicked?.Invoke(this, EventArgs.Empty);
            };
        }

        #endregion
    }
}

解决方案

最优方案:使用Interaction.Triggers添加触发器

无需重新定义Style,完全保留CustomButton默认样式,直接通过System.Windows.Interactivity库的扩展触发器实现逻辑:

<controls:CustomButton
    Command="{Binding ValidateCommand}"
    Content="Validate"
    Color="{StaticResource PrimarySolidColorBrush}">
    <i:Interaction.Triggers>
        <i:DataTrigger Binding="{Binding ElementName=Amount, Path=(Validation.HasError)}" Value="True">
            <ei:ChangePropertyAction PropertyName="IsEnabled" Value="False" />
        </i:DataTrigger>
    </i:Interaction.Triggers>
</controls:CustomButton>

需要在XAML头部添加对应命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

备选方案:修改CustomButton内部样式结构

将CustomButton内部的样式目标类型改为自身,并设置为隐式样式(移除x:Key),这样外部可以通过BasedOn继承默认样式:

<Button.Resources>
    <!-- 保留原有Tooltip样式和ControlTemplate -->
    <Style TargetType="{x:Type local:CustomButton}">
        <Setter Property="Template" Value="{StaticResource ButtonControlTemplate}" />
        <Setter Property="Padding" Value="4"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="FontSize" Value="12"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Foreground" Value="White"/>
    </Style>
</Button.Resources>

外部使用时,通过BasedOn继承默认样式并添加触发器:

<controls:CustomButton.Style>
    <Style TargetType="{x:Type controls:CustomButton}" BasedOn="{StaticResource {x:Type controls:CustomButton}}">
        <Setter Property="IsEnabled" Value="True" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding ElementName=Amount, Path=(Validation.HasError)}" Value="True">
                <Setter Property="IsEnabled" Value="False" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</controls:CustomButton.Style>

总结

如果完全不想使用BasedOn,优先选择Interaction.Triggers方案,无需修改控件内部结构,直接扩展触发器逻辑且保留默认样式。

内容的提问来源于stack exchange,提问作者Ludovic Wagner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:54:58