【问题标题】:Underline Label in WPF, using stylesWPF中的标签下划线,使用样式
【发布时间】:2010-12-01 19:52:55
【问题描述】:

我有以下风格:

<Style x:Key="ActionLabelStyle" TargetType="{x:Type Label}">
    <Setter Property="Margin" Value="10,3" />
    <Setter Property="Padding" Value="0" />
    <Setter Property="TextBlock.TextWrapping" Value="Wrap" />
    <Setter Property="FontFamily" Value="Calibri" />
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsMouseOver" Value="True" />
                <Condition Property="IsEnabled" Value="True" />
            </MultiTrigger.Conditions>
            <Setter Property="Background" Value="Red" />
            <Setter Property="TextBlock.TextDecorations" Value="Underline" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

所以基本上,我想要一个标签,当它被启用并且鼠标光标在它上面时,它带有下划线。这种风格中不起作用的部分是&lt;Setter Property="TextBlock.TextDecorations" Value="Underline" /&gt;。现在,我在这里做错了什么?感谢所有的帮助。

【问题讨论】:

    标签: wpf xaml styles label


    【解决方案1】:

    一个老问题,但由于我刚刚解决了这个问题,这就是我的方法。虽然它只是使用触发器而不是 MultiTrigger

    对于 XAML:

    <Label Content="This text is for testing purposes only.">
        <Style TargetType="{x:Type ContentPresenter}">
            <Style.Resources>
                <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="TextDecorations" Value="Underline" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Style.Resources>
        </Style>
    </Label>
    

    对于 C# 代码隐藏:

    public override void OnApplyTemplate()
    {
        if( !hasInitialized )
        {
            var tbUnderStyle = new Style( typeof(TextBlock), (Style)FindResource(typeof(TextBlock)) );
            var tbUnderSetter = new Setter( TextBlock.TextDecorationsProperty, TextDecorations.Underline );
            var tbUnderTrigger = new Trigger() { Property = Label.IsMouseOverProperty, Value = true };
            tbUnderTrigger.Setters.Add( tbUnderSetter );
            var contentPresenter = FindVisualChild<ContentPresenter>( this );
            contentPresenter.Resources.Add( typeof(TextBlock), tbUnderStyle );
    
            hasInitialized = true;
        }
    }
    

    hasInitialized 是一个在构造函数中设置为 false 的 bool,而 FindVisualChild 来自 here

    【讨论】:

      【解决方案2】:

      只是为了将我的解决方法添加到组合中。我目前正在使用 C# 代码,它运行良好。我只是捕获 MouseLeave 和 MouseEnter 事件并在那里显示下划线。

      void Control_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
      {
          WPFHelper.EnumerateChildren<TextBlock>(this, true).ForEach(c => c.TextDecorations = null);
      }
      
      void Control_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
      {
          WPFHelper.EnumerateChildren<TextBlock>(this, true).ForEach(c => c.TextDecorations = TextDecorations.Underline);
      }
      

      WPFHelper 类简单地枚举 DependencyObject 的所有子级,而 ForEach 是一个扩展方法,它只是执行每个项目的 lambda 表达式内的操作。

      【讨论】:

        【解决方案3】:

        进一步 Jerry 的回答,为了避免每次都必须将 TextBlock 添加到模板中,您可以通过将 Setter 属性添加到样式中来让样式也这样做:

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Label}">
                    <TextBlock>
                        <ContentPresenter />
                    </TextBlock>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        

        那么你的标签又回到了:

        <Label Content="Test!" Style="{StaticResource ActionLabelStyle}" />
        

        谢谢杰瑞!

        安德鲁。

        【讨论】:

        • 这看起来不错 - 但 ContentPresenter 在该位置似乎无效
        【解决方案4】:

        这实际上比看起来要困难得多。在 WPF 中,Label 不是 TextBlock。它派生自 ContentControl,因此可以在其 Content 集合中托管其他非文本控件。

        但是,您可以指定一个字符串作为内容,如下例所示。在内部,将构建一个 TextBlock 来为您托管文本。

        <Label Content="Test!"/>
        

        这在内部翻译为:

            <Label>
                <Label.Content>
                    <TextBlock>
                        Test!
                    </TextBlock>
                </Label.Content>
            </Label>
        

        对此的简单解决方案是将 TextBlock 的 TextDecorations 属性作为附加属性。例如,FontSize 就是这样设计的,所以下面的工作:

            <Label TextBlock.FontSize="24">
                <Label.Content>
                    <TextBlock>
                        Test!
                    </TextBlock>
                </Label.Content>
            </Label>
        

        TextBlock.FontSize 附加属性可应用于可视树中的任何位置,并将覆盖树中任何 TextBlock 后代上该属性的默认值。但是,TextDecorations 属性不是这样设计的。

        这让您至少有几个选择。

        1. 使用颜色、边框、光标等,而不是带下划线的文本,因为这 100% 更容易实现。
        2. 更改您执行此操作的方式,改为将样式应用于 TextBlock。
        3. 麻烦创建your own attached property 和控制模板来尊重它。
        4. 执行以下操作以嵌套作为您的样式的子项出现的 TextBlock 的样式:

        仅供参考,这是迄今为止我在 WPF 中做过的最丑陋的事情,但它确实有效!

            <Style x:Key="ActionLabelStyle" TargetType="{x:Type Label}">
                <Setter Property="Margin" Value="10,3" />
                <Setter Property="Padding" Value="0" />
                <Setter Property="TextBlock.TextWrapping" Value="Wrap" />
                <Setter Property="FontFamily" Value="Calibri" />
                <Style.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsMouseOver" Value="True" />
                            <Condition Property="IsEnabled" Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Background" Value="Red" />
                    </MultiTrigger>
                </Style.Triggers>
                <Style.Resources>
                    <Style TargetType="TextBlock">
                        <Style.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Label}, Path=IsMouseOver}" Value="True" />
                                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="True" />
                                </MultiDataTrigger.Conditions>
                                <Setter Property="TextDecorations" Value="Underline"/>
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </Style.Resources>
            </Style>
        

        之所以有效,是因为它覆盖了该样式标签下的任何 TextBlock 的默认样式。然后它使用 MultiDataTrigger 允许相对绑定备份到 Label 以检查其 IsMouseOver 属性是否为 True。呸。

        编辑:

        请注意,这仅在您明确创建 TextBlock 时才有效。当我发布这个时我是不正确的,因为我已经弄脏了我的测试标签。嘘。感谢 Anvaka 指出这一点。

            <Label Style="{StaticResource ActionLabelStyle}">
                <TextBlock>Test!</TextBlock>
            </Label>
        

        这行得通,但如果你不得不去这个麻烦,你只是太努力了。要么有人会发布更聪明的东西,要么正如你所说,我的选项 1 现在看起来不错。

        【讨论】:

        • 杰瑞,你真的在​​这里做了很多工作。感谢你付出的努力。我想我会选择你推荐的第一个选项。您提供的样式可以解决问题,但正如您所说,它也很“糟糕” - 确实很糟糕。我将您的答案标记为正确。再次感谢您的回复!
        • 谢谢,鲍里斯。为了得到一个 SO 答案,我确实比平时做了更多的工作,但是与此类似的问题已经困扰了我一段时间,所以我所做的研究对我自己的启迪也很有帮助。 :)
        • +1 -- 很好的回答杰瑞。我学到了一些东西,即使我希望我没有:)
        • 好答案:)。您不需要在触发器中有 IsEnabled 属性。如果它被禁用,它不会收到 MouseOver 事件。如果您在标签中正确设置内容,它也将不起作用:,所以要小心。
        • 谢谢,安瓦卡。我更新了答案以包含您的见解。我为没有正确测试而感到羞耻。我已经用它下面的显式 TextBlock 更新了我的标签。
        【解决方案5】:

        我认为问题在于TextBlock.TextDecorations 没有在Label 上定义。

        如果您愿意使用TextBlock 而不是Label,可以使用this approach

        【讨论】:

          猜你喜欢
          • 2012-02-26
          • 1970-01-01
          • 2012-10-12
          • 2017-11-19
          • 2013-06-29
          • 1970-01-01
          • 2022-11-24
          • 2010-09-07
          相关资源
          最近更新 更多