Search code examples
c#wpfmvvmexpression-blend

How to create a Textbox and Label with sloped border?


I am trying do create a Textbox in Wpf that has a Label in its top left corner and addiotionally this label shall have a border with a slope on one side.

https://i.sstatic.net/tA3Do.jpg The way I tried it

Now for one or two specific cases that was doable with a workaround where i just used lines for the border. Now that i want to use it a bit more I need to do it the right way, especially in a way where it is scalable.

I would be really happy if someone could point me in the right direction.

Edit: So the code I am using after taking into account the responses so far, which i created as a user control:

<Grid Height="93" Width="335">
<TextBox TextWrapping="Wrap" Text="{Binding TextboxText}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" BorderBrush="{x:Null}" Background="{x:Null}"/>
<Path Data="M384,242 L442.5,242" HorizontalAlignment="Left" Height="1" Margin="0,28.667,0,0" Stretch="Fill" VerticalAlignment="Top" Width="59.5">
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#8EFFFFFF"/>
<GradientStop Color="White" Offset="0.991"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Stroke>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<LinearGradientBrush.RelativeTransform>
<TransformGroup>                                        <ScaleTransform CenterY="0.5" CenterX="0.5"/>                                   <SkewTransform CenterY="0.5" CenterX="0.5"/>                                <RotateTransform Angle="90" CenterY="0.5" CenterX="0.5"/>                       <TranslateTransform/>
</TransformGroup>
</LinearGradientBrush.RelativeTransform>
<GradientStop Color="White" Offset="0.009"/>
<GradientStop Color="#5FFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
</Path>
<Label Content="{Binding LabelText}" HorizontalAlignment="Left" Width="113" FontSize="16" Height="40" VerticalAlignment="Top" BorderBrush="White" Margin="0,0.167,0,0"/>
<Path Data="M125.12574,28.672087 L145.37561,-1.1668457" HorizontalAlignment="Left" Height="30.839" Margin="58.125,-1,0,0" Stretch="Fill" VerticalAlignment="Top" Width="21.25">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#51FFFFFF" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#49FFFFFF" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Path Data="M0,83 L181.35815,83" Fill="#FFF4F4F5" Height="1" Stretch="Fill" VerticalAlignment="Bottom" Width="327" StrokeThickness="2" Margin="0,0,10,10">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
</Path>
</Grid>

It works and the only thing that is still bothering me is the resizability of the label border, which is quite annoying to do but luckily not necessary in my case.


Solution

  • This is another solution which uses a Style instead of a UserControl.

    I assume the Label is the description of the TextBox, inside the style, I created a TextBlock (replacing the Label as it would be an overkill in this case) of which Text is bound to the Tag of parent TextBox. Then it will display whatever you put in your Tag.

    Also I have grouped TextBlock and two Paths into a Grid, set its columns to be auto-sized so you won't have the resizability issue anymore.

    The screenshot below is two TextBoxes with different labels. enter image description here

    The TextBox Style

    <Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
        <Setter Property="Background" Value="Black" />
        <Setter Property="Foreground" Value="#FFB8B8B8" />
        <Setter Property="BorderBrush" Value="#FF484848" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Padding" Value="1" />
        <Setter Property="AllowDrop" Value="true" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
        <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border x:Name="BottomLine" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,0,0,1">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Grid x:Name="TopPanel" HorizontalAlignment="Left">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <TextBlock d:LayoutOverrides="Width, Height" x:Name="Caption" TextWrapping="Wrap" Text="{TemplateBinding Tag}" FontSize="14.667" VerticalAlignment="Center" Margin="4,0,24,0" />
                                <Path x:Name="BottomPath" Data="M384,242 L442.5,242" Stretch="Fill" VerticalAlignment="Bottom" Margin="0,0,-1.3,0">
                                    <Path.Stroke>
                                        <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                                            <LinearGradientBrush.RelativeTransform>
                                                <TransformGroup>
                                                    <ScaleTransform CenterY="0.5" CenterX="0.5" />
                                                    <SkewTransform CenterY="0.5" CenterX="0.5" />
                                                    <RotateTransform Angle="90" CenterY="0.5" CenterX="0.5" />
                                                    <TranslateTransform />
                                                </TransformGroup>
                                            </LinearGradientBrush.RelativeTransform>
                                            <GradientStop Color="White" Offset="0.009" />
                                            <GradientStop Color="#5FFFFFFF" Offset="1" />
                                        </LinearGradientBrush>
                                    </Path.Stroke>
                                </Path>
                                <Path x:Name="RightPath" Data="M125.12574,28.672087 L145.37561,-1.1668457" Stretch="Fill" Grid.Column="1">
                                    <Path.Stroke>
                                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                            <GradientStop Color="#51FFFFFF" Offset="0" />
                                            <GradientStop Color="White" Offset="1" />
                                        </LinearGradientBrush>
                                    </Path.Stroke>
                                </Path>
                            </Grid>
                            <Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true" Margin="0,0,0,-0.001" d:LayoutOverrides="Width, Height" Grid.Row="1">
                                <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            </Microsoft_Windows_Themes:ListBoxChrome>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    The TextBoxes

        <TextBox Tag="Label" Text="This is a textbox" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center" FontSize="24" Style="{DynamicResource MyTextBoxStyle}"/>
        <TextBox Tag="Long Label" Text="This is a textbox" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Bottom" FontSize="24" Style="{DynamicResource MyTextBoxStyle}"/> 
    

    Hope this helps. :)