25

I have a Border with CornerRadius property set to 10. Inside that Border, there's a StackPanel. The panel contains two Borders with blue and red backgrounds, respectively.

The upper left and upper right corners of the blue border and the lower left and lower right corners of the red border are sticking out of the curved edges of the first border. I wish to make the blue and red borders trim to the parent border. Is that possible?

By the way, I do know that if I set the same value for CornerRadius property of the blue and red borders, it will follow the curve of the first one. I don't want that - I want trimming. Thanks!

<Border 
    Width="200" 
    Height="200" 
    BorderThickness="1" 
    BorderBrush="Black"
    CornerRadius="10">
    <StackPanel>
        <Border Height="100" Background="Blue" />
        <Border Height="100" Background="Red" />
    </StackPanel>
</Border>
H.B.
  • 166,899
  • 29
  • 327
  • 400
Boris
  • 9,986
  • 34
  • 110
  • 147

3 Answers3

28

You may write a converter for the Clip property. Converter should implement IMultiValueConverter and bound to actual size and corner radius, for example.

public class BorderClipConverter : IMultiValueConverter
{
    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;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Usage:

<Border CornerRadius="10">
    <Border.Clip>
        <MultiBinding Converter="{StaticResource BorderClipConverter}">
            <Binding Path="ActualWidth"
                        RelativeSource="{RelativeSource Self}"/>
            <Binding Path="ActualHeight"
                        RelativeSource="{RelativeSource Self}"/>
            <Binding Path="CornerRadius"
                        RelativeSource="{RelativeSource Self}"/>
        </MultiBinding>
    </Border.Clip>
</Border>
Community
  • 1
  • 1
Marat Khasanov
  • 3,808
  • 19
  • 22
  • Best example I have been able to find! – Sebastian Edelmeier Dec 18 '13 at 12:37
  • I thought about doing something like this , but wondered if there is a convinent XAML way of doing so . i'll try this out . my question on the matter : http://stackoverflow.com/questions/24158147/wpf-clipping-a-border – eran otzap Jun 11 '14 at 09:34
  • This could be done with a regular converter (i.e. not a multivalue converter) then binding to itself. It would make the binding much prettier (i.e. much more easy to use repeatedly) and would push the logic of grabbing the width/height/radius into the converter. – claudekennilol Jun 03 '16 at 16:06
  • @claudekennilol, can't agree with you, because pushing this logic to converter require to implement property change listener which is very non-trivial task. – Marat Khasanov Jun 05 '16 at 09:43
  • That's a good point depending on the content. The example given in the question and a lot of other uses wouldn't have this problem but there are definitely some that would. – claudekennilol Jun 06 '16 at 18:11
  • I made an example of this with the clip taking into account all of the different corner radiuses [here](https://gist.github.com/anonymous/c37ce2826e214fdeee84250140adfdc6) – Lauraducky Jan 31 '18 at 02:53
15

There's also a XAML-only solution to your problem by using the OpacityMask property. The trick is to create a Grid inside the outer Border and set the Grid's OpacityMask to another element that acts as a clipping mask.

<Border Width="200" Height="200"
        BorderThickness="1" BorderBrush="Black"
        CornerRadius="10">
    <Grid>
        <Grid.OpacityMask>
            <VisualBrush Visual="{Binding ElementName=clipMask}" Stretch="None" />
        </Grid.OpacityMask>
        <Border x:Name="clipMask" Background="White" CornerRadius="10" />
        <StackPanel Background="White">
            <Border Height="100" Background="Blue" />
            <Border Height="100" Background="Red" />
        </StackPanel>
    </Grid>
</Border>

In the snippet above I used a Border as clipping mask, but it can also be another element as long as its fill color is non-transparent. Note also that the clipMask Border also has the same CornerRadius.

Inspired by: http://www.codeproject.com/Articles/225076/Creating-Inner-Shadows-for-WPF-and-Silverlight

pogosama
  • 1,818
  • 1
  • 24
  • 29
1

ClipToBounds is the property that might help in this case.

Edit: After some testing i noticed that ClipToBounds only cares about the actual bounds (i.e. the rectangular area the control uses), so content still sticks out at the corners...

This seems to suggest that simple clipping to the border is not possible. You could set the Clip property to a rounded rectangle, this is not very convenient though because its size cannot be bound i think.

Your options seem to be using an OpacityMask together with a VisualBrush or recreating a clipping whenever relevant properties change by using a MultiBinding & MultiValueConverter...

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • OpacticyMask only excepts Geometry type , so you would still need some converter like in @MaratKhasanov 's question – eran otzap Jun 11 '14 at 09:35