Clipping a border in WPF Clipping a border in WPF wpf wpf

Clipping a border in WPF


There is a ClipToBounds property on the Border class that should clip the content at the bounds of the Border, but unfortunately, it doesn't 'do what it says on the tin':

<Border CornerRadius="25" BorderBrush="RoyalBlue" BorderThickness="3" Width="300"     Height="50" ClipToBounds="True"> <!-- This doesn't work as expected -->    <Rectangle Fill="SkyBlue" /></Border>

However, the Rectangle class also provides some properties that could help. Is there something stopping you from just using the Rectangle.RadiusX and Rectangle.RadiusY properties to round the Rectangle corners?:

<Border CornerRadius="25" BorderBrush="RoyalBlue" BorderThickness="3" Width="300"     Height="50">    <Rectangle RadiusX="23" RadiusY="23" Fill="SkyBlue" /></Border>

I'm aware that you want to clip the coloured fill of the Rectangle, but you could use the Rectangle.Clip property for that:

<Border CornerRadius="25" BorderBrush="RoyalBlue" BorderThickness="3" Width="300"     Height="50">    <Grid>        <Rectangle Name="ClipRectangle" Fill="Green" Margin="50,0,0,0"             Visibility="Hidden" />        <Rectangle RadiusX="23" RadiusY="23" Fill="SkyBlue" Clip="{Binding             RenderedGeometry, ElementName=ClipRectangle}" />    </Grid></Border>

This clips the coloured Rectangle with the RenderedGeometry of the other Rectangle named ClipRectangle... or when I say this clips, perhaps I should have said this is supposed to clip as I've just discovered that this only appears to work in the WPF Designer and not when the application is run.

enter image description here

However, I'm out of time here, so hopefully you can find the final piece of the puzzle and complete this yourself. Potentially, you could also complete this by data binding to the GradientStop.Offset property of a LinearGradientBrush that is set as the Background on the Border, so you wouldn't even need a Rectangle for this method. I'll have another look if I can later.


UPDATE >>>

I had another look at this Clip Rectangle and can't work out why it only works in the Visual Studio Designer. So, giving up with that idea, you can try the LinearGradientBrush idea instead, which is equally good. First, define your Brush:

<LinearGradientBrush x:Key="ValueBrush" StartPoint="0,0" EndPoint="1,0">    <GradientStop Offset="0.0" Color="SkyBlue" />    <GradientStop Offset="0.7" Color="SkyBlue" />    <GradientStop Offset="0.7" Color="Transparent" />    <GradientStop Offset="1.0" Color="Transparent" /></LinearGradientBrush>

For now, I've hardcoded the values in to produce this:

enter image description here

From just this code:

<Border CornerRadius="25" BorderBrush="RoyalBlue" Background="{StaticResource     ValueBrush}" BorderThickness="3" Width="300" Height="50" ClipToBounds="True" />

For your actual requirements, you'd need to create a double property to data bind to the GradientStop.Offset property like this:

<LinearGradientBrush x:Key="ValueBrush" StartPoint="0,0" EndPoint="1,0">    <GradientStop Offset="0.0" Color="SkyBlue" />    <GradientStop Offset="{Binding MidPoint}" Color="SkyBlue" />    <GradientStop Offset="{Binding MidPoint}" Color="Transparent" />    <GradientStop Offset="1.0" Color="Transparent" /></LinearGradientBrush>

Now, as long as you provide a value between 0.0 and 1.0, this will create your level meter.


A much better solution using an OpacityMask , All The Template Parts aside from the OuterBorder Placed in "MainGrid" are Clipped using an Opacity mask which is set by an object aside it called "MaskBorder".

"TemplateRoot" exist for the inner workings of the PrograssBar control.

  <ControlTemplate TargetType="{x:Type ProgressBar}">        <Grid x:Name="TemplateRoot">            <Border x:Name="OuterBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="10">                <Grid>                    <Border x:Name="MaskBorder" Background="{TemplateBinding Background}" CornerRadius="9.5" />                    <Grid x:Name="MainGrid">                        <Grid.OpacityMask>                            <VisualBrush Visual="{Binding ElementName=MaskBorder}" />                                                           </Grid.OpacityMask>                        <Rectangle x:Name="PART_Track" Fill="White" />                        <Border x:Name="PART_Indicator" HorizontalAlignment="Left">                                                             <Grid x:Name="Foreground">                                <Rectangle x:Name="Indicator" Fill="{TemplateBinding Background}" />                                <Grid x:Name="Animation" ClipToBounds="true">                                    <Rectangle x:Name="PART_GlowRect" Fill="#FF86C7EB" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100" />                                </Grid>                            </Grid>                        </Border>                                                                                       </Grid>                                                                                                                                                     </Grid>                                     </Border>                               </Grid>                         </ControlTemplate>


What i ended up doing :

ControlTemplate :

         <ControlTemplate TargetType="{x:Type ProgressBar}">                <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">                    <Rectangle x:Name="PART_Track" Fill="White" />                    <Border x:Name="roundBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="10"/>                    <Border x:Name="PART_Indicator" HorizontalAlignment="Left">                        <Border.Clip>                            <MultiBinding Converter="{x:Static common:UIConverters.BorderClipConverter}">                                <Binding Path="ActualWidth" ElementName="roundBorder" />                                <Binding Path="ActualHeight" ElementName="roundBorder" />                                <Binding Path="CornerRadius" ElementName="roundBorder" />                            </MultiBinding>                        </Border.Clip>                        <Grid x:Name="Foreground">                            <Rectangle x:Name="Indicator" Fill="{TemplateBinding Background}" />                            <Grid x:Name="Animation" ClipToBounds="true">                                <Rectangle x:Name="PART_GlowRect" Fill="#FF86C7EB" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100" />                            </Grid>                        </Grid>                    </Border>                </Grid>                   </ControlTemplate>

BorderClipConverter : ( from Marat Khasanov answer ) with some fine tuning in the Rect

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];            var clip = new RectangleGeometry(new Rect(1.5, 1.5, width - 3, height - 3), 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();    }}