在 WPF 中裁剪边框

Posted

技术标签:

【中文标题】在 WPF 中裁剪边框【英文标题】:Clipping a border in WPF 【发布时间】:2014-08-01 05:29:18 【问题描述】:

我需要创建一个圆形ProgressBar 模板。

控制模板:

<ControlTemplate TargetType="x:Type ProgressBar">
   <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">                      
     <Rectangle x:Name="PART_Track" Margin="1" Fill="White" />

     <Border x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="1"  >
        <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>

     <Border x:Name="roundBorder" BorderBrush="TemplateBinding BorderBrush"
             BorderThickness="TemplateBinding BorderThickness" CornerRadius="10" />
     <TextBlock />

  </Grid>
</ControlTemplate>

这导致:

PART_Indicator 是左侧的 LightBlue 矩形(其宽度在 ProgressBar 控件内部设置,如此处所示,值为 20)和 roundBorder

我需要将PART_Indicator 夹在roundBorder 上,结果如下:

【问题讨论】:

检查this 问题。它可以帮助你 顺便说一句,任何人都在看这个问题。下面的答案。遗憾的是,0 票赞成。效果很好。这是要走的路。没有转换器。基于控件的内容对模板没有额外的影响。在保留的同时进行简单的剪辑。 @gt 哪一个? 【参考方案1】:

Border 类上有一个ClipToBounds 属性应该Border 的边界处剪辑内容,但不幸的是,它并没有按照它所说的那样做锡:

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

不过,Rectangle 类也提供了一些可以提供帮助的属性。是否有什么阻止您仅使用Rectangle.RadiusXRectangle.RadiusY 属性来圆角Rectangle?:

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

我知道您想要剪裁 Rectangle 的彩色填充,但您可以使用 Rectangle.Clip 属性:

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

这会将彩色Rectangle 与另一个名为RectangleRenderedGeometry 剪辑在一起,名为ClipRectangle...或者当我说这个剪辑时,也许我应该说这应该是剪辑,因为我刚刚发现这似乎只在 WPF 设计器中工作,而不是在应用程序运行时工作。

但是,我没时间了,所以希望你能找到最后一块拼图并自己完成。潜在地,您还可以通过将数据绑定到 LinearGradientBrushGradientStop.Offset 属性来完成此操作,该属性设置为 Border 上的 Background,因此对于此方法,您甚至不需要 Rectangle。以后可以的话我再看看。


更新>>>

我再次查看了这个Clip Rectangle,但不明白为什么它只适用于 Visual Studio 设计器。所以,放弃这个想法,你可以试试LinearGradientBrush 的想法,同样不错。首先,定义你的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>

现在,我已经硬编码了这些值来生成这个:

仅从这段代码:

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

根据您的实际需求,您需要创建一个double 属性来将数据绑定到GradientStop.Offset 属性,如下所示:

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

现在,只要您提供介于 0.0 和 1.0 之间的值,这将创建您的电平表。

【讨论】:

这正是我想要的,谢谢。我试试看 我实际上无法让它工作,我会在+1分钟后尝试【参考方案2】:

使用 OpacityMask 更好的解决方案,除了放置在“MainGrid”中的 OuterBorder 之外的所有模板部分都使用 Opacity 蒙版进行裁剪,该蒙版由名为“MaskBorder”的对象设置。

“TemplateRoot”存在于 PrograssBar 控件的内部工作中。

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

【讨论】:

嗨@Sheridan,我找到了一个比我拥有的解决方案更好的解决方案。【参考方案3】:

我最终做了什么:

控制模板:

         <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:(来自Marat Khasanov answer)在矩形中进行了一些微调

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();
    
 

【讨论】:

以上是关于在 WPF 中裁剪边框的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GraphicsMagick 从图像中裁剪/调整背景/边框的大小

使用边框裁剪图像

干预裁剪后的 png 图像周围的黑色边框

边框半径 + 背景颜色 == 裁剪边框

ImageMagick 基于矩形边框颜色裁剪图像

旋转图像并裁剪黑色边框