在 WPF 中裁剪边框
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24158147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Clipping a border in WPF
提问by eran otzap
I need to create a round ProgressBartemplate.
我需要创建一个圆形ProgressBar模板。
ControlTemplate :
控制模板:
<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>
This results in :
这导致:


Where PART_Indicatoris the LightBlue rectangle on the left (its width is set internally in the ProgressBarcontrol, as seen here with a value of 20) and the roundBorder.
PART_Indicator左侧的 LightBlue 矩形在哪里(其宽度在ProgressBar控件内部设置,如此处所示,值为 20)和roundBorder.
What I need is for the the PART_Indicatorto clip over the roundBorder, resulting in something like:
我需要的是PART_Indicator将 夹在 上roundBorder,结果如下:


采纳答案by eran otzap
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".
使用 OpacityMask 一个更好的解决方案,除了放置在“MainGrid”中的 OuterBorder 之外的所有模板部分都使用不透明蒙版进行裁剪,该蒙版由一个名为“MaskBorder”的对象设置。
"TemplateRoot" exist for the inner workings of the PrograssBar control.
“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>
回答by Sheridan
There is a ClipToBoundsproperty on the Borderclass that shouldclip the content at the bounds of the Border, but unfortunately, it doesn't 'do what it says on the tin':
有一个ClipToBounds对房地产Border类应夹在的边界的内容Border,但不幸的是,它并没有“做它在锡说”:
<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 Rectangleclass also provides some properties that could help. Is there something stopping you from just using the Rectangle.RadiusXand Rectangle.RadiusYproperties to round the Rectanglecorners?:
但是,Rectangle该类还提供了一些可以提供帮助的属性。有什么可以阻止您仅使用Rectangle.RadiusX和Rectangle.RadiusY属性来绕过Rectangle拐角的吗?:
<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.Clipproperty for that:
我知道您想剪辑 的彩色填充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>
This clips the coloured Rectanglewith the RenderedGeometryof the other Rectanglenamed ClipRectangle... or when I say this clips, perhaps I should have said this is supposed to clipas I've just discovered that this only appears to work in the WPF Designer and not when the application is run.
这剪辑的颜色Rectangle与RenderedGeometry另一个Rectangle命名ClipRectangle...或者当我说这个剪辑时,也许我应该说这应该剪辑,因为我刚刚发现这似乎只在 WPF 设计器中工作,而不是当应用程序运行。


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.Offsetproperty of a LinearGradientBrushthat is set as the Backgroundon the Border, so you wouldn't even need a Rectanglefor this method. I'll have another look if I can later.
但是,我没时间在这里,所以希望您能找到拼图的最后一块并自己完成。潜在地,您还可以通过将数据绑定到设置为on 的GradientStop.Offset属性来完成此操作,因此您甚至不需要此方法的 a 。如果可以的话,我稍后再看看。LinearGradientBrushBackgroundBorderRectangle
UPDATE >>>
更新 >>>
I had another look at this Clip Rectangleand can't work out why it only works in the Visual Studio Designer. So, giving up with that idea, you can try the LinearGradientBrushidea instead, which is equally good. First, define your Brush:
我又看了一遍,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>
For now, I've hardcoded the values in to produce this:
现在,我已经对值进行了硬编码以生成以下内容:


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 doubleproperty to data bind to the GradientStop.Offsetproperty like this:
对于您的实际要求,您需要创建一个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>
Now, as long as you provide a value between 0.0 and 1.0, this will create your level meter.
现在,只要您提供一个介于 0.0 和 1.0 之间的值,这将创建您的电平表。
回答by eran otzap
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
BorderClipConverter :(来自Marat Khasanov 的回答)在 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();
}
}

