如何使边框修剪子元素?

25

我有一个带有 CornerRadius 属性设置为 10 的 Border。在这个 Border 中,有一个 StackPanel。该面板包含两个具有蓝色和红色背景的 Border

蓝色边框的左上角和右上角以及红色边框的左下角和右下角从第一个边框的曲线边缘处突出。我想使蓝色和红色边框修剪到父边框。是否可能实现?

顺便说一句,我知道如果将蓝色和红色边框的 CornerRadius 属性设置为相同的值,它会遵循第一个边框的曲线。但我不想要那种效果 - 我想要修剪。谢谢!

<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.
3个回答

28
你可以编写一个转换器来处理Clip属性。转换器应该实现IMultiValueConverter接口,并绑定到实际大小和角度半径,例如。
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();
    }
}

使用:

<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>

我找到的最好的例子! - Sebastian Edelmeier
我考虑过像这样做,但想知道是否有一种方便的XAML方式来实现。我会尝试一下。关于这个问题,我的问题链接是:https://dev59.com/JIDba4cB1Zd3GeqPFYhp - eran otzap
这可以通过常规转换器(即非多值转换器)完成,然后绑定到自身。这将使绑定更加美观(即更易于重复使用),并将获取宽度/高度/半径的逻辑推入转换器中。 - claudekennilol
@claudekennilol,我不同意你的观点,因为将这个逻辑推到转换器需要实现属性更改监听器,这是非常棘手的任务。 - Marat Khasanov
这是一个很好的观点,取决于内容。在问题中给出的例子和许多其他用法都不会有这个问题,但肯定有一些会有。 - claudekennilol
我已经制作了一个示例,考虑到所有不同的角半径,您可以在此处查看(https://gist.github.com/anonymous/c37ce2826e214fdeee84250140adfdc6)。 - Lauraducky

16

使用OpacityMask属性存在一个仅限于XAML的解决方案来解决您的问题。诀窍是在外部边框内创建一个Grid,并将Grid的OpacityMask设置为另一个充当剪切蒙版的元素。

<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>

在上面的片段中,我使用了一个Border作为剪辑蒙版,但只要其填充颜色是非透明的,它也可以是另一个元素。还要注意,clipMask Border也具有相同的CornerRadius

灵感来自:http://www.codeproject.com/Articles/225076/Creating-Inner-Shadows-for-WPF-and-Silverlight


1

ClipToBounds 是在这种情况下可能有所帮助的属性。

编辑:经过一些测试,我注意到 ClipToBounds 只关心实际边界(即控件使用的矩形区域),因此内容仍然会在角落处突出...

这似乎表明简单地剪切到边框是不可能的。您可以将 Clip 属性设置为圆角矩形,但这并不方便,因为它的大小不能绑定。

您的选择似乎是使用 OpacityMask 与 VisualBrush 或者通过使用 MultiBinding & MultiValueConverter 在相关属性更改时重新创建剪辑...


OpacticyMask只接受几何类型,因此您仍需要像@MaratKhasanov的问题中那样使用一些转换器。 - eran otzap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接