WPF Custom Border - bracket style - wpf

I would like to create a custom border around a content, that the upper and lower side of the border is not continuous, it should have a style like bracket as below. (inside the "bracket border" the content should be placed e.g. grid, stackpanel, etc.)
Note that the height of the right and the left border can be changed depending on the content's height, whereas the top and the bottom should have a standard width.
[ sample content ]
In order to achieve this, I separated the view in a 3 columns grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22px"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="22px"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="Blue" BorderThickness="6px 6px 0px 6px"></Border>
<Border Grid.Column="1" BorderBrush="Transparent" BorderThickness="6px">
<!--Place for the actual content of the border-->
<TextBlock Text="Test" FontSize="15"></TextBlock>
</Border>
<Border Grid.Column="2" BorderBrush="Blue" BorderThickness="0px 6px 6px 6px"></Border>
</Grid>
Is there another approach for achieving this style?

One possible solution is to write your own Border based on a Decorator.
An implementation (for a different border) can be found in How can I draw a border with squared corners in wpf?

One simple trick is try setting some LinearGradientBrush for the BorderBrush. If your text has a fixed width, it will look best at all time. However when the text's width may change, the horizontal lines (at 2 ends) will shrink/extend at some ratio. That's because we set some Offset for the GradientStop and it's pity that this Offset can only be set based on some ratio (from 0 to 1) with the width of the whole Brush (which is exactly the width of the Border when the background is stretched). Note that the MappingMode cannot change this behavior, it just works for StartPoint and EndPoint.
Here is the pure XAML code:
<Border VerticalAlignment="Center" HorizontalAlignment="Center"
BorderThickness="3" Padding="5,0,5,0">
<TextBlock Text="Square bracket border here" FontSize="30"
HorizontalAlignment="Center"/>
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,1">
<GradientStop Offset="0.03" Color="Blue"/>
<GradientStop Offset="0.03" Color="Transparent"/>
<GradientStop Offset="0.97" Color="Transparent"/>
<GradientStop Offset="0.97" Color="Blue"/>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
You can change the Offset of the first 2 GradientStops to what you want, the Offset of the remaining GradentStops should be the subtraction of the first's Offset from 1.
If using some code behind, you can pinpoint exactly the length of the horizontal lines (at the 2 ends). That way we need some Binding between the Offset and the ActualWidth of the Border. Next we need some Converter here, this Converter will convert the ActualWidth and the desired exact length to the correct ratio. So when the text width changes, the length of the horizontal lines will always be some fixed value.

You can try the following XAML code:
<Grid>
<Border BorderBrush="Black" BorderThickness="1"/>
<TextBlock Text="sample content"/>
<Border BorderBrush="White" BorderThickness="0,1,0,1" Margin="8,0,8,0"/>
</Grid>
Second border's color "White" can be replaced with the actual background color. "Transparent" color will not help.
Thanks,
RDV

Related

How to add drop shadow to just one specific side in WPF?

I am trying to achieve a grid, with a shadow on just one side and no trace of any shadow on any of the other sides. I tried fiddling around with the direction property of the DropShadowEffect.
What I have tried:
<Grid Background="Transparent" Grid.Row="0" Grid.Column="1">
<Grid Background="White"/>
<Border CornerRadius="0,5,0,0" BorderBrush="White" BorderThickness="0" Background="White">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Direction="355" RenderingBias="Quality" ShadowDepth="2"/>
</Border.Effect>
</Border>
</Grid>
</Grid>
This is what happens with my code:
I want to achieve a drop shadow only visible on the bottom side of the grid, and no trace of the shadow on any of the other sides. The above code leaves a thin gray trail on the left side, which wouldn't work for me.
Sorry if this is a silly question, I am kinda new to WPF.
I don't think the DropShadowEffect has any functionality built-in for this sort of application, however, I managed to achieve the required result using a rectangle and filling it with a linear gradient.
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Opacity="0.3">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#00131313" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
To maintain the same width as the parent of the shadow, add them to the same grid and the same column, set the horizontal and vertical alignment to stretch and the shadow will look consistent.
Then I positioned the rectangle in place of the shadow. Seems a little wanky, but works nonetheless.
Edit:
I found another solution which seems way more better, using the ClipToBounds property and the BorderThickness property.
<Border ClipToBounds="True" BorderBrush="White" BorderThickness="0,2,0,0">
<Border.Effect>
<DropShadowEffect ShadowDepth="2" BlurRadius="10"/>
</Border.Effect>
</Border>
Using a border and a drop shadow is easier than using a rectangle and tweaking it till it looks natural.
Usage of grids is advised to position the border perfectly.

Annoying 1-pixel band between Border and Label?

I am trying to get rid of an annoying 1-pxel band of un-drawn background between a border and a label control.
Specifically, my XAML looks like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Viewbox Margin="0,0,5,3" HorizontalAlignment="Right" Grid.Column="1">
<Border BorderThickness="2" CornerRadius="2" BorderBrush="#BF272727" Background="#BF272727">
<Label Padding="-3" Content="Testing" Foreground="Red"/>
</Border>
</Viewbox>
</Grid>
And my output looks like this:
Notice that the border brush and the label background both have a semi-transparent color. I've fiddled with other properties of both the border and the label and haven't found anything that will cause this single pixel between the border and label to get filled with the background color. I imagine it is something simple, but I haven't stumbled on it yet. I've tried setting the Background property on the label instead of the border, but with no change in results.
WPF's device-agnostic rendering works very well most of the time, but yes…occasionally you'll see artifacts like this, due to adjacent elements being anti-aliased, causing pixels that nominally should be exactly the same color having some variation.
In your case, you should omit the border thickness and brush altogether. To do that and still have the same visual appearance, you need to include padding, so that the border element is expanded out to the size it would have had, had there been a non-zero thickness border. And of course, without the border included, the corner radius needs to be adjusted up (by half the original border thickness).
The following produces the result you seem to want:
<Border Background="#BF272727" CornerRadius="3" Padding="2">
<Label Padding="-3" Content="Testing" Foreground="Red"/>
</Border>
Turn on aliased edges in either your Border control or any of its parents:
RenderOptions.EdgeMode="Aliased"

Can width of TextBox be set using FormattedText.MinWidth?

Banging my head trying to figure this out. The goal is to create a wrapping TextBox at a given location where the width is set to that of the largest word in the string of text. FormattedText.MinWidth calculates the width of the largest word in the string. But passing that MinWidth to the TextBox causes the TextBox to be slightly too narrow. TextBlock does not have this problem.
Evidentally something is happening deep down inside the TextBox causing this behavior. I can't just add a fixed magic number to the TextBox width because the increase in the width needed to correct the problem will always differ based on the pixel width of the character that was wrapped to the next line. The number of pixels will always differ depending on what that character is, the font, and font size.
If someone has more reputation could you please add FormattedText and MinWidth as a tag? Restrictions won't let me, a stupid first post newbe, do this. I also would like to have added an image which would make this sooo much easier to understand but stupid restrictions (did I say that?) prevent me from doing so.
namespace FormattedTextExample
{
public partial class FormattedText1 : Window
{
string noteText =
"We hold these truths to be self-evident, that all men are created equal, that they " +
"are endowed by their Creator with certain unalienable Rights, that among these are " +
"Life, Liberty and the pursuit of Happiness.--That to secure these rights, Governments " +
"are instituted..";
public FormattedText1()
{
InitializeComponent();
myText.Text = noteText;
}
protected override void OnRender(DrawingContext drawingContext)
{
FormattedText ft = new FormattedText(
textToFormat: noteText,
culture: CultureInfo.GetCultureInfo("en-us"),
flowDirection: myText.FlowDirection,
typeface: new Typeface(myText.FontFamily.ToString()),
emSize: myText.FontSize,
foreground: myText.Foreground);
ft.MaxTextWidth = ft.MinWidth;
DrawingContext dc = drawDest.Open();
dc.DrawText(ft, new Point(0, 0));
dc.Close();
myDrawingBrush.Drawing = drawDest;
myText.Width = ft.MinWidth;
}
}
}
<Window x:Class="FormattedTextExample.FormattedText1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="FormattedText" Height="500" Width="500">
<DockPanel>
<Grid DockPanel.Dock="Top" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0">
<Rectangle.Fill>
<DrawingBrush x:Name="myDrawingBrush" Stretch="None"
AlignmentY="Top" AlignmentX="Left" >
<DrawingBrush.Drawing>
<DrawingGroup x:Name="drawDest" />
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel Grid.Column="1">
<TextBox x:Name="myText" TextWrapping="Wrap" />
<!-- Everything works fine if using TextBlock -->
<!--<TextBlock x:Name="myText" TextWrapping="Wrap" />-->
</StackPanel>
</Grid>
</DockPanel>
</Window>
The reason your textbox needs more space is the way its control template is defined.
This is how the default control template looks like (from MSDN):
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border"
BorderThickness="1"
CornerRadius="2"
Padding="2">
<Border.Background>
<SolidColorBrush Color="{DynamicResource ControlLightColor}" />
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<!-- VisualStateManager code -->
<ScrollViewer x:Name="PART_ContentHost" Margin="0" />
</Border>
</ControlTemplate>
Studying the default control template or defining your own helps determining exactly how much extra space do you need to allocate to your text box.

Multiple nested borders with rounded corners. How to?

I'd like to display a content enclosed by multiple borders of different colors and with rounded corners. I experimented with corner radius but could never get perfect corners (i.e. adjacent to corners of a parent border) on nested borders. What is the recommended practice in this case, if any?
The following is what I tried:
<Border Grid.Row="1" Grid.Column="1" CornerRadius="3" BorderThickness="3" BorderBrush="#FAFAFA"Background="#FAFAFA" Margin="40,40,40,40" Padding="0">
<Border.Effect>
<DropShadowEffect Color="#1f40618c" />
</Border.Effect>
<StackPanel>
<Border CornerRadius="3" BorderThickness="3" BorderBrush="#696B6D" Background="#696B6D" Margin="0" Padding="0" >
<Border CornerRadius="2.3" BorderThickness="3" BorderBrush="Black" Margin="0" Padding="32,32,32,16" Background="#102F8C">
<Grid />
</Border>
</Border>
</StackPanel>
</Border>
Thanks.
You have to take into account that the BorderThickness adds to the total width or height of a Border control. You can think of it as if half of the border line lies inside the control and half lies outside. On the other hand the CornerRadius is given relative to the center of the border line.
Consequently the difference between the inner and outer radius should be equal to the sum of half of both BorderThicknesses (plus perhaps the outer Padding and the inner Margin).
With your nested borders that have a BorderThickness of 3 each and an outer CornerRadius of also 3 that would result in an inner CornerRadius of 0.
You may simply set the inner CornerRadius to some sensible value and add 3 to that value for the outer one:
<Border BorderBrush="Blue" BorderThickness="3" CornerRadius="6">
<Border BorderBrush="Red" BorderThickness="3" CornerRadius="3">
</Border>
</Border>

WPF Border Thickness increase direction

Hey. Another WPF question. In my XAML code I have a border:
<Border x:Name="myBorder" Background="AliceBlue"
Width="200" Height="200"
BorderThickness="10" BorderBrush="Black">
</Border>
and somewhere in code I increase the BorderThickness
double thickness = myBorder.BorderThickness.Bottom + 2;
myBorder.BorderThickness = new Thickness(thickness);
and the result is that the border's weight increases but not outside the 200x200 width-height, but inner, decreasing the dimension. Is there a way to do the opposite?
Well, actually you should set the width and height on the inner or outer control of the border, not on the border itself. Then you can set a negative margin for the border, equal to minus the value of the border thickness. Something like this should to the trick:
<Border x:Name="myBorder" Background="AliceBlue"
Margin="-10,-10,-10,-10" BorderThickness="10" BorderBrush="Black">
<Button Background="Red" Content="Test" Width="200" Height="200"></Button>
</Border>
It looks like you need to increase Width and Height accordingly.

Resources