<Border ClipToBounds="True" CornerRadius=20>
<Grid>
<Canvas>
<Ellipse Fill="Red" Height="100" Width="100"/>
</Canvas>
<ContentPresenter/>
</Grid>
</Border>
UPD:感谢您的回复。找到解决方案 除边框外使用它。不幸的是,不透明蒙版不适用于Canvas几何体:(
没有装饰者(即边框)或布局面板(即 Stackpanel)具有开箱即用的这种行为。
ClipToBounds用于布局。 ClipToBounds不会阻止元素 从界外抽出;它只是阻止孩子们的布局 来自'溢出'。
此外,大多数元素都不需要ClipToBounds = True,因为 他们的实现不允许他们的内容的布局溢出 无论如何。最值得注意的例外是Canvas。
最后,Border将圆角视为内部的绘图 布局的界限。
public class ClippingBorder : Border
{
protected override void OnRender(DrawingContext dc)
{
OnApplyChildClip();
base.OnRender(dc);
}
public override UIElement Child
{
get
{
return base.Child;
}
set
{
if (Child != value)
{
if (Child != null)
{
// Restore original clipping
Child.SetValue(ClipProperty, _oldClip);
}
if (value != null)
{
_oldClip = value.ReadLocalValue(ClipProperty);
}
else
{
// If we dont set it to null we could leak a Geometry object
_oldClip = null;
}
base.Child = value;
}
}
}
protected virtual void OnApplyChildClip()
{
UIElement child = Child;
if (child != null)
{
_clipRect.RadiusX = _clipRect.RadiusY = Math.Max(0.0, this.CornerRadius.TopLeft - (this.BorderThickness.Left * 0.5));
_clipRect.Rect = new Rect(Child.RenderSize);
child.Clip = _clipRect;
}
}
private RectangleGeometry _clipRect = new RectangleGeometry();
private object _oldClip;
}
答案 0 :(得分:1)
我遇到了类似的问题,我发现答案是使用ScrollViewer的OpacityMask。请尝试以下方法:
<Grid>
<Border ClipToBounds="True" CornerRadius="20"
Background="Green">
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Border Background="Green"
SnapsToDevicePixels="True"
CornerRadius="{Binding CornerRadius, RelativeSource={RelativeSource FindAncestor,AncestorType=Border}}"
Width="{Binding ActualWidth,RelativeSource={RelativeSource FindAncestor, AncestorType=Border}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor,AncestorType=Border}}"/>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
<ScrollViewer>
<Canvas>
<Ellipse Fill="Red" Height="100" Width="100" Canvas.Left="-37" Canvas.Top="-26"/>
<ContentPresenter/>
</Canvas>
</ScrollViewer>
</Border>
</Grid>
希望它对你有所帮助。
答案 1 :(得分:0)
如果Border具有固定大小,您只需将其Clip
属性设置为适当的RectangleGeometry:
<Border CornerRadius="20" Background="Green">
<Border.Clip>
<RectangleGeometry RadiusX="20" RadiusY="20" Rect="0,0,100,50"/>
</Border.Clip>
<Grid>
<Canvas>
<Ellipse Fill="Red" Height="100" Width="100"/>
</Canvas>
<ContentPresenter/>
</Grid>
</Border>
遗憾的是,如果没有绑定转换器,您无法绑定RectangleGeometry的Rect
属性。
答案 2 :(得分:0)
如果设置Border.ClipToBounds="True"
,其内容实际上将被剪切到其边界,但是说&#34;界限&#34;不考虑Border.CornerRadius
- 界限基本上是一个源自Border.ActualWidth
和Border.ActualHeight
的矩形(不仅适用于Border
,而且适用于任何派生的元素来自UIElement
)。
如果您需要将Border
的内容剪切为自定义形状(圆角属于该类别),则应通过Border.Clip
属性指定剪裁几何(如@Clemens所述)在他的回答中)。
现在,根据各种条件,您可以提出具有不同复杂性的解决方案,但我要介绍通用解决方案。我们的想法是根据Border
的大小和角半径创建一个合适的几何体。为了实现这一目标,我们将创建IMultiValueConverter
并使用MultiBinding
连接它。这是完整的转换器:
public class ClipConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)values[0];
var height = (double)values[1];
if (width == 0 || height == 0) return null;
var corners = (CornerRadius)values[2];
//First we calculate break points in case corner radii exceed
//the size of the Border - this is done in a proportional manner
var topBreak = width * corners.TopLeft / (corners.TopLeft + corners.TopRight);
var rightBreak = height * corners.TopRight / (corners.TopRight + corners.BottomRight);
var bottomBreak = width * corners.BottomLeft / (corners.BottomLeft + corners.BottomRight);
var leftBreak = height * corners.TopLeft / (corners.TopLeft + corners.BottomLeft);
//Let's name interesting points on the path:
// 0-------1
// / \
// 7 2
// | |
// 6 3
// \ /
// 5-------4
//We need to take into account the break points
var p0 = new Point(Math.Min(corners.TopLeft, topBreak), 0);
var p1 = new Point(Math.Max(width - corners.TopRight, topBreak), 0);
var p2 = new Point(width, Math.Min(corners.TopRight, rightBreak));
var p3 = new Point(width, Math.Max(height - corners.BottomRight, rightBreak));
var p4 = new Point(Math.Max(width - corners.BottomRight, bottomBreak), height);
var p5 = new Point(Math.Min(corners.BottomLeft, bottomBreak), height);
var p6 = new Point(0, Math.Max(height - corners.BottomLeft, leftBreak));
var p7 = new Point(0, Math.Min(corners.TopLeft, leftBreak));
var geometry = new StreamGeometry();
//Draw the geometry using a StreamGeometryContext object
var context = geometry.Open();
context.BeginFigure(p0, true, true);
if (p1 != p0)
context.LineTo(p1, false, false);
context.ArcTo(p2, new Size(p2.Y, width - p1.X), 90, false, SweepDirection.Clockwise, false, false);
if (p3 != p2)
context.LineTo(p3, false, false);
context.ArcTo(p4, new Size(height - p3.Y, width - p4.X), 90, false, SweepDirection.Clockwise, false, false);
if (p5 != p4)
context.LineTo(p5, false, false);
context.ArcTo(p6, new Size(height - p6.Y, p5.X), 90, false, SweepDirection.Clockwise, false, false);
if (p7 != p6)
context.LineTo(p7, false, false);
context.ArcTo(p0, new Size(p7.Y, p0.X), 90, false, SweepDirection.Clockwise, false, false);
//Close the context so that the geometry can be rendered
context.Close();
return geometry;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
用法:
<Border (...)>
<Border.Resources>
<local:ClipConverter x:Key="ClipConverter" />
</Border.Resource>
<Border.Clip>
<MultiBinding Converter="{StaticResource ClipConverter}">
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" />
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Border.Clip>
(...)
</Border>