WPF中的下划线标签,使用样式

时间:2009-09-27 17:56:20

标签: wpf xaml styles label

我有以下风格:

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

所以基本上,我希望有一个标签,当它启用并且鼠标光标在它上面时加下划线。这种风格不起作用的部分是<Setter Property="TextBlock.TextDecorations" Value="Underline" />。现在,我在这里做错了什么?谢谢你的帮助。

5 个答案:

答案 0 :(得分:23)

这实际上比它看起来要困难得多。在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. 执行以下操作以嵌套显示为样式子项的TextBlocks的样式:
  5. 仅供参考,这是我迄今为止在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>
    

    这是有效的,因为它覆盖了此样式的Label下面的任何TextBlock的默认样式。然后,它使用MultiDataTrigger允许相对绑定回到Label,以检查其IsMouseOver属性是否为True。呸。

    修改

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

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

    这很有效,但如果你不得不去解决这个问题,那你就是在努力工作。要么有人会发布更聪明的东西,要么就像你说的那样,我的选择1现在看起来很不错。

答案 1 :(得分:4)

除了Jerry的回答之外,为了避免每次通过在样式中添加Setter属性而让样式执行此操作时必须将TextBlock添加到模板中:

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

然后你的标签回到:

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

谢谢杰瑞!

安德鲁。

答案 2 :(得分:3)

我认为问题在于TextBlock.TextDecorations未定义Label

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

答案 3 :(得分:0)

只是将我的解决方法添加到组合中。我目前正在使用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表达式中的操作。

答案 4 :(得分:0)

一个老问题,但是因为我只是为此而奋斗,这是我的方法。虽然它只使用Trigger而不是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#Codebehind:

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,而来自here的FindVisualChild。