Multiple nested borders with rounded corners. How to? - wpf

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>

Related

WPF Custom Border - bracket style

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

WPF DPI rendering issues with two borders

Enviroment
Win 8, VS 2012, .NET 4, WPF, screen native resolution 1920x1080#96DPI
XAML
<Border BorderThickness="1" BorderBrush="Red" Width="20" Height="20">
<Border BorderThickness="1" BorderBrush="Blue" />
</Border>
The problem
The borders look fine when DPI is 96, but if I change the DPI to 120, the borders are no longer perfectly pixel aligned.
What I have tried
1) Setting the following properties - RenderOptions.EdgeMode="Aliased" SnapsToDevicePixels="True" UseLayoutRounding="True" - on both borders, mitigates the anti-aliasing and bleeding issues from the higher DPI, but the inner border seems to have a 1px margin usually on the right and bottom sides.
2) The following code does not help to work around the problem:
<Grid Width="20" Height="20">
<Border BorderThickness="1" BorderBrush="Red" />
<Border BorderThickness="1" BorderBrush="Blue" Margin="1" />
</Grid>
3) Using Rectangles instead of Borders has the same problem.
4) The problem also persists under a guest Win 7 running in VirtualBox.
5) Edit - This looks a bit better, in that there is no inner margin, but the outer "border" is 2px thick:
<Canvas Width="20" Height="20">
<Polygon Points="0,0 20,0, 20,20, 0,20" StrokeThickness="1" Stroke="Red" RenderOptions.EdgeMode="Aliased" SnapsToDevicePixels="True" UseLayoutRounding="True" />
<Polygon Points="1,1 19,1, 19,19, 1,19" StrokeThickness="1" Stroke="Blue" RenderOptions.EdgeMode="Aliased" SnapsToDevicePixels="True" UseLayoutRounding="True" />
</Canvas>
Question(s)
How do I get the borders to be pixel perfect without any inner margins or anti-alising/bleeding?
I cannot use code-behind to rework the borders sizes when DPI is other than 96. I must stick to XAML only, because I'm trying to create vector icons (based on XAML).
Bravo Microsoft - https://connect.microsoft.com/VisualStudio/feedback/details/798513/wpf-dpi-rendering-issues-with-two-borders
Yet again, this issue will not be addressed nor fixed.

Validation.ErrorTemplate size

I've got the following control template which I use as a Validation.ErrorTemplate for TextBoxes:-
<ControlTemplate x:Key="ControlValidationErrorTemplate">
<DockPanel LastChildFill="True">
<Border Background="Red"
DockPanel.Dock="right"
Padding="2,0,2,0"
ToolTip="{Binding ElementName=valAdorner, Path=AdornedElement.(Validation.Errors), Converter={x:Static val:ValidationErrorsConverter.Instance}}">
<TextBlock Text="!"
VerticalAlignment="center"
HorizontalAlignment="center"
FontWeight="Bold"
Foreground="white" />
</Border>
<AdornedElementPlaceholder x:Name="valAdorner"
VerticalAlignment="Center">
<Border BorderBrush="red"
BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
When a TextBox contains invalid content, the above template applies a red border and adds a red box containing an exclamation mark immediately to the right of the TB.
The problem is, the exclamation mark overlaps anything immediately to the right of the TB, rather than the layout changing to accomomodate the exclamation mark. I have a similar problem in DataGrids - the exclamation mark overlaps the right-hand edge of the containing cell, rather than the column width increasing to accommodate it.
Using Snoop, it appears that the template is being displayed in an "adorner layer" which I assume is a separate visual tree? This would explain why the window's layout isn't recalculated to take into account the exclamation mark. Can anyone suggest a way to achieve what I want?
As I suspected, it's due to the error template being rendered on the adorner layer, so it doesn't affect the layout of the window. See: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/9de3c9e5-5759-4f88-9184-175d3eaabdad/
I'm now using this control template instead:-
<ControlTemplate x:Key="ControlValidationErrorTemplate">
<Grid>
<Polygon Points="9,9 9,0 0,0"
Stroke="Red"
StrokeThickness="1"
Fill="Red"
HorizontalAlignment="Right"
VerticalAlignment="Top"
ToolTip="{Binding ElementName=valAdorner, Path=AdornedElement.(Validation.Errors), Converter={x:Static val:ValidationErrorsConverter.Instance}}" />
<AdornedElementPlaceholder x:Name="valAdorner"
VerticalAlignment="Center">
<Border BorderBrush="red"
BorderThickness="1" />
</AdornedElementPlaceholder>
</Grid>
</ControlTemplate>
This draws a red border around the control, with a small red triangle overlapping the top-right corner of the control - hovering over this displays a tooltip containing the error message.

Wpf - Drop shadow disappears

I created a window with no style and added a custom border. I need my window to drop a shadow.
<Border BorderBrush="#000000 "
BorderThickness="1,0,1,1" Width="400"
Height="400"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
CornerRadius="10, 10, 0, 0"
Background="Black">
<Border.Effect>
<DropShadowEffect Color="Black" Direction="320"
ShadowDepth="5" Opacity="1" BlurRadius="5" />
</Border.Effect></Border>
But my shadow disappears when I set the width and height like this :
Width="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=Width}"
Help me find a solution please. Thanks
Since we don't have the full scope of your solution, I must assume that the Border is within a ControlTemplate that is assigned to the Style of the Window. Remember that if you are going to apply a DropShadowEffect to the root window you need to pad it otherwise you won't see it. Apply Padding="10" to Border and you should see it.
Try To add on the X & Y Thickness of the Shadow to your Windows dimensions
Don't apply effects on the root visuals! In this case, they apply to all children down visual tree and will be cause of reducing performance.
Use the next pattern:
<Grid x:Name="RootWindowGrid">
<Border x:Name="BorderWithEffect" Margin="5">
<Border.Effect>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="5" Opacity="1" BlurRadius="5" />
</Border.Effect>
</Border>
<Border x:Name="RootBorder_ForAll_Another_Window_Visuals" Margin="5"/>
</Grid>
Margin depends on effect intensivity.

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