【问题标题】:Default TextBlock style overriding button text color默认 TextBlock 样式覆盖按钮文本颜色
【发布时间】:2026-01-24 08:05:01
【问题描述】:

我的问题出现在 .NET 3.5 SP1 中的 WPF 上,可以描述如下:

我有一个默认的Style 在我的 UI 中点击所有 TextBlock 元素。这就是它的外观:

<Style TargetType="{x:Type TextBlock}">
   <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
   <Setter Property="Foreground" Value="Red"/>
</Style>

这适用于所有TextBlocks。除此之外,我还有一个Button 样式,包括一个看起来像这样(缩短)的ControlTemplate

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}" BasedOn="{x:Null}">
   <Setter Property="Foreground" Value="Green"/>
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type Button}">
            <Border x:Name="Border" 
                    Background="{TemplateBinding Background}" 
                    BorderThickness="{TemplateBinding BorderThickness}" 
                    Padding="{TemplateBinding Padding}" 
                    Height="24" 
                    BorderBrush="{TemplateBinding BorderBrush}">
               <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                 VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                 TextBlock.Foreground="{TemplateBinding Foreground}"/>
            </Border>
            <ControlTemplate.Triggers>...</ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

注意ContentPresenter 中的TextBlock.Foreground="{TemplateBinding Foreground}" 行。这应该将按钮文本设置为绿色,事实上它在 Visual Studio 的设计器视图中也是如此。但是当我编译并运行程序时,按钮文本是红色的,文本颜色由默认的TextBlock 样式设置。我用 Snoop 验证了这一点。

如何防止默认TextBlock 样式覆盖TextBlock.Foreground 值? ContentPresenterOverridesDefaultStyle 属性在这种情况下没有帮助。

有什么想法吗?

【问题讨论】:

    标签: c# wpf xaml styles textblock


    【解决方案1】:

    关于2个选项:

    1. @Fredrik Hedblad

    尝试将其添加到 ResourceDictionary

    <DataTemplate DataType="{x:Type sys:String}">
        <TextBlock Text="{Binding}">
            <TextBlock.Resources> 
                <Style TargetType="{x:Type TextBlock}"/>
            </TextBlock.Resources>
        </TextBlock>
    </DataTemplate>
    
    1. @Ivan Krivyakov

    最好不要覆盖 TextBlock 的默认样式。到目前为止,我能想到的最好的想法是为 Control 创建一个样式并将其应用于所有*窗口。

    我会建议一种替代方法并使用附加的依赖属性,例如

    namespace AttachedProperties
    {
      public static class TextBlockExtensions
      {
        public static bool GetUseAppThemeStyle(DependencyObject obj)
        {
          return (bool)obj.GetValue(UseAppThemeStyleProperty);
        }
        public static void SetUseAppThemeStyle(DependencyObject obj, bool value)
        {
          obj.SetValue(UseAppThemeStyleProperty, value);
        }
        // Using a DependencyProperty as the backing store for UseAppThemeStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UseAppThemeStyleProperty =
            DependencyProperty.RegisterAttached("UseAppThemeStyle", typeof(bool), typeof(TextBlockExtensions), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
      }
    }
    

    注意:您可以将其设置为默认为真或假

    然后拥有命名空间:

    xmlns:attachedProperties="clr-namespace:AttachedProperties"
    

    制作默认样式:

       <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
        <Style.Triggers>
          <DataTrigger Binding="{Binding Path=(attachedProperties:TextBlockExtensions.UseAppThemeStyle), RelativeSource={RelativeSource Mode=Self}}" Value="True">
            <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
            <Setter Property="Foreground" Value="Red"/>
          </DataTrigger>
        </Style.Triggers>
      </Style>
    

    然后,如果您需要获取默认值,您可以在样式中设置附加属性:

      <Style x:Key="blueButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
        <Setter Property="attachedProperties:TextBlockExtensions.UseAppThemeStyle" Value="False" />
        <Setter Property="Foreground" Value="Blue" />
      </Style>
    

    或直接在按钮上:

    <Button attachedProperties:TextBlockExtensions.UseAppThemeStyle="False" Foreground="Blue">I'm blue da ba dee da ba die...</Button>
    

    【讨论】:

      【解决方案2】:

      最好不要覆盖 TextBlock 的默认样式。到目前为止,我能想到的最好的想法是为 Control 创建一个样式并将其应用于所有*窗口。

      <!-- App.xaml -->
      <Application.Resources>
          <Style x:Key="RedStyle" TargetType="{x:Type Control}">
              <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
              <Setter Property="Foreground" Value="Red"/>
          </Style>
      </Application.Resources>
      
      <!-- MainWindow.xaml -->
      <Window Style="{StaticResource RedStyle}" ...>
          ...
      </Window>
      

      更多详情请看这里:http://www.ikriv.com/dev/dotnet/wpftextstyle/

      【讨论】:

        【解决方案3】:

        this link查看答案5

        发生这种情况是因为 ContentPresenter 创建一个 TextBlock 对于字符串内容,并且因为 TextBlock 不在可视化树中,它 将查找应用程序级别 资源。如果你定义一种风格 应用程序中的 TextBlock 级别,然后将其应用于 ContentControl 中的这些 TextBlock

        一种解决方法是定义一个 System.String 的 DataTemplate,其中 我们可以显式使用默认值 TextBlock 来显示内容。你 可以将该 DataTemplate 放在 你定义的同一个字典 TextBlock 样式使这 DataTemplate 将应用于 无论 ContentPresenter 受什么影响 你的风格。

        尝试将其添加到 ResourceDictionary

        <DataTemplate DataType="{x:Type sys:String}">
            <TextBlock Text="{Binding}">
                <TextBlock.Resources> 
                    <Style TargetType="{x:Type TextBlock}"/>
                </TextBlock.Resources>
            </TextBlock>
        </DataTemplate>
        

        【讨论】:

        • 感谢您的回答。有两件事我不明白: 1. 即使 ContentPresenter 创建的 TextBlock 不在 VisualTree 中,它仍然应该接受我在 TextBlock.Foreground 中输入的值,对吗? 2. 通过使用您描述的 DataTemplate,我只能对 ContentPresenter 元素中的所有文本使用一种样式,对吗?
        • @HA:据我了解,它只会确保其他 TextBlock 样式不会被拾取。但我可能是错的,我以前用过它,我没有遇到任何问题
        • @HA:此外,由于 ContentControl 的内容,这只会在创建 TextBlock 时使用。您仍然可以做您在示例中所做的事情,例如设置前景等。还是我误解了这个问题?
        • 不,你是对的。一开始我并没有完全理解。但是我决定不使用这个解决方案,因为如果我在按钮内放置一个图像或矩形或其他东西,它会失败。相反,我从默认的 TextBlock 样式中删除了 Foreground 属性。这种方式对我来说效果更好。尽管如此,还是感谢您的帮助。
        • 覆盖 System.String 的数据模板是一个坏主意,因为它失去了下划线到键盘快捷键和 ContentStringFormat 功能。这让我陷入了热水。在此处查看更多详细信息:ikriv.com/dev/dotnet/wpftextstyle/StringTemplate.shtml