简体   繁体   中英

How to use a custom style trigger to draw a Line in a TextBox control

I have a custom textbox control in which I want to draw a horizontal line when a certain property is true and then remove the line when the property is false. The line needs to have a width that is -2 of the control width and be at a height of -5 from the control bottom. I am currently using an Adorner with the following OnRender to accomplish this behavior:

protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
    base.OnRender(drawingContext);

    var rect = new Rect(AdornedElement.RenderSize);
    rect.Inflate(-2, -5);

    // ColoredPen is a custom class that holds a Pen object
    drawingContext.DrawLine(ColoredPen.Pen, rect.BottomLeft, rect.BottomRight);
}

I am running into some issues where the Adorner is not refreshing properly and so it disappears even when the related property is true . I have tried several different ways of ensuring the Adorner is refreshed but it is failing miserably. I believe the Adorner approach is not really the proper approach to my problem. I think it would be better if I used a ControlTemplate with the property as a trigger. For simple example case, we can use the IsFocused property. This is the code that I have thus far, but I am unsure as to how to set the X and Y properties of the Line and really how to make it work. This current code just crashes the application (and yes, I know that the dimensions for the line set in this code are not what I want, this was my attempt to just make the line appear, I figured I could tweak it from there).

<Style x:Key="TextBoxUnderlineTemplate" TargetType="myUi:MyTextBox">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="myUi:MyTextBox">
                <Line Name="UnderlineStuff" Stroke="Black" X1="0"
                     X2="{Binding RelativeSource={RelativeSource Self}, Path=Width}"
                     Y1="{Binding RelativeSource={RelativeSource Self}, Path=Height}"
                     Y2="{Binding RelativeSource={RelativeSource Self}, Path=Height}"
                     StrokeThickness="2" Visibility="Collapsed"/>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsFocused" Value="True">
                        <Setter TargetName="UnderlineStuff" Property="Visibility" Value="Visible"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

So, I am looking for a way to make this work with a ControlTemplate, or if there is another suggestion of how to make this work, that would also be acceptable (an Adorner is out). Note that once this is working I will need to be able to add a second line a little lower than this line depending on an additional property.

This presents an underline on IsMouseOver . You may need to improve the very simple converter implementation, which is based on font size:

在此处输入图片说明

XAML:

<Window x:Class="WpfApplication363.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication363"
    mc:Ignorable="d"
    Title="MainWindow" Height="300" Width="300">

<Window.Resources>

    <local:MyConverter x:Key="conv1"/>

    <SolidColorBrush x:Key="TextBox.Static.Border" Color="#FFABAdB3"/>
    <SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FF7EB4EA"/>
    <SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF569DE5"/>

    <Style x:Key="TextBoxStyle1" TargetType="{x:Type TextBox}">
        <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
        <Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Grid>
                        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                        </Border>
                        <Line x:Name="line1" 
                              Y1="{TemplateBinding FontSize, Converter={StaticResource conv1}}" 
                              X2="{Binding ElementName=border, Path=ActualWidth}" 
                              Y2="{TemplateBinding FontSize, Converter={StaticResource conv1}}" 
                              Stroke="Red" 
                              Visibility="Hidden"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
                            <Setter Property="Visibility" TargetName="line1" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocused" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
                    <Condition Property="IsSelectionActive" Value="false"/>
                </MultiTrigger.Conditions>
                <Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
            </MultiTrigger>
        </Style.Triggers>
    </Style>

</Window.Resources>

<Grid>

    <TextBox Style="{DynamicResource TextBoxStyle1}" 
             HorizontalAlignment="Center" 
             VerticalAlignment="Center" 
             FontSize="24"
             Text="Textbox with underline !"/>

</Grid>

Converter:

public class MyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ((double)value) + 4;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM