7

I have a simple window containing an outer border with a corner radius, and an inner border with a background. The border is basically just a placeholder for any type of content I would like to place inside the rounded corner outer border.

<Window x:Class="TestRunner.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" AllowsTransparency="True" 
        WindowStyle="None" Background="{x:Null}" >
    <Border BorderThickness="2" BorderBrush="Black" CornerRadius="8"  >     
           <Border Background="White">

           </Border>
    </Border>
</Window>

The problem is that the inner control does not inherit the rounded corner so it draws on top of the rounded corner, like this:

Bad corner rendering

How do I adjust my outer control, so inner controls do not attempt to draw on top of the rounded corner.

Setting rounded corner on the inner control is not a viable option as it will lead to horrible duplication of corner radius.

Pete
  • 12,206
  • 8
  • 54
  • 70

3 Answers3

5

I'm assuming that you have a Border within a Border just to illustrate the problem. If possible, just avoid the issue altogether by not including any control within the outer Border that renders anything in the corners.

If you must include a control that renders something in the corners, you could use a Clip:

<Border x:Name="border" CornerRadius="10">
    <Border.Clip>
        <RectangleGeometry Width="{Binding ActualWidth, ElementName=border}" Height="{Binding ActualHeight, ElementName=border}" RadiusX="10" RadiusY="10"/>
    </Border.Clip>

    <Border Background="White"/>
</Border>

Another option (depending on your exact scenario) might be to place the outer Border "above" the other content. As long as it has a transparent Fill and IsHitTestVisible set to false, it may be sufficient:

<Grid>
    <Border Background="White"/>
    <Border CornerRadius="10" BorderBrush="Black" BorderThickness="3" Fill="Transparent" IsHitTestVisible="false"/>
</Grid>
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
2

RectangleGeometry does not have a Width property, at least in WPF.

For my needs, I had to create a IMultiValueConverter as described here : https://stackoverflow.com/a/5650367/2663813

After that, I still had a problem at the corners, so I used Kent's second solution (Note that Border.Fill doesn't exist either).

Here is a what I wrote:

<Grid>
    <Border x:Name="canvasBorder" CornerRadius="5">
        <Border.Resources>
            <tools:ContentClipConverter x:Key="ContentClipConverter" />
        </Border.Resources>
        <Border.Clip>
            <MultiBinding Converter="{StaticResource ContentClipConverter}">
                <Binding Path="ActualWidth"
                RelativeSource="{RelativeSource Self}"/>
                <Binding Path="ActualHeight"
                RelativeSource="{RelativeSource Self}"/>
                <Binding Path="CornerRadius"
                RelativeSource="{RelativeSource Self}"/>
            </MultiBinding>
        </Border.Clip>
        <!-- ... -->
    </Border>
    <Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Background="Transparent" IsHitTestVisible="false" />
</Grid>

and inside the ContentClipConverter.cs:

/// <summary>
/// Clips the content of a rounded corner border.
/// Code taken from <a href="https://stackoverflow.com/a/5650367/2663813">this topic</a>
/// </summary>
public class ContentClipConverter : IMultiValueConverter {
    /// <summary>
    /// Gets a clipping geometry for the item
    /// </summary>
    /// <param name="values">The input values</param>
    /// <param name="targetType">The parameter is not used.</param>
    /// <param name="parameter">The parameter is not used.</param>
    /// <param name="culture">The parameter is not used.</param>
    /// <returns>The clipping geometry</returns>
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
        if (values.Length == 3 && values[0] is double && values[1] is double && values[2] is CornerRadius) {
            var width = (double)values[0];
            var height = (double)values[1];

            if (width < double.Epsilon || height < double.Epsilon) {
                return Geometry.Empty;
            }

            var radius = (CornerRadius)values[2];

            // Actually we need more complex geometry, when CornerRadius has different values.
            // But let me not to take this into account, and simplify example for a common value.
            var clip = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft);
            clip.Freeze();

            return clip;
        }

        return DependencyProperty.UnsetValue;
    }

    /// <summary>
    /// Not implemented
    /// </summary>
    /// <param name="value">The parameter is not used.</param>
    /// <param name="targetTypes">The parameter is not used.</param>
    /// <param name="parameter">The parameter is not used.</param>
    /// <param name="culture">The parameter is not used.</param>
    /// <returns>This function does not return anything</returns>
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
        throw new NotSupportedException();
    }
Community
  • 1
  • 1
cube45
  • 3,429
  • 2
  • 24
  • 35
0

One possibility might be to put some padding on the outer border:

<Border BorderThickness="2" BorderBrush="Black" CornerRadius="8" Padding="4">
    ....
</Border>

but this might lead to too much white space in your application.

ChrisF
  • 134,786
  • 31
  • 255
  • 325