Reputation: 47
I have a polygon shape being generated in code from external data and drawn to a Canvas
using a PointCollection
. On top of that from the user selected point of the polygon I am overlaying a circle (Ellipse)
of a user selectable size.
What I need to do next is have the bounds of the circle constrained by the perimeter of the polygon. so that the user can vary the size of the circle to part fill the polygon or fully fill it, if made large enough. With any part of the circle outside of the polygon bounds clipped.
Can any one help me with this? Thank you.
Upvotes: 1
Views: 1162
Reputation: 34275
You need to use a Clip
property of your ellipse. You can use your polygon's RenderedGeometry
property as the source for the clip geometry. However, considering you position them inside a canvas, you'll need to take their position into consideration.
Let's create a multi-value converter which clips one shape with another:
public class ClipWithConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var clipped = (Shape)values[0];
var clip = (Shape)values[1];
var clippedPos = new Point(Canvas.GetLeft(clipped), Canvas.GetTop(clipped));
var clipPos = new Point(Canvas.GetLeft(clip), Canvas.GetTop(clip));
var deltaVector = clipPos - clippedPos;
var geometry = clip.RenderedGeometry.Clone();
geometry.Transform = new TranslateTransform(deltaVector.X, deltaVector.Y);
return geometry;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Now we can use it in XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<local:ClipWithConverter x:Key="ClipWithConverter"/>
</Window.Resources>
<Canvas>
<Ellipse x:Name="Ellipse" Canvas.Left="50" Canvas.Top="0"
Fill="Red" Width="250" Height="250">
<Ellipse.Clip>
<MultiBinding Converter="{StaticResource ClipWithConverter}">
<Binding ElementName="Ellipse"/>
<Binding ElementName="Polygon"/>
</MultiBinding>
</Ellipse.Clip>
</Ellipse>
<Polygon x:Name="Polygon" Canvas.Left="10" Canvas.Top="10"
Stroke="Black" StrokeThickness="3"
Points="100 0, 300 150, 150 300, 0 100"/>
</Canvas>
</Window>
However, our converter gets called a little too early, when no geometry is rendered yet, so let's add a little hack into constructor:
public MainWindow()
{
InitializeComponent();
Dispatcher.InvokeAsync(
BindingOperations.GetMultiBindingExpression(Ellipse, ClipProperty).UpdateTarget,
DispatcherPriority.Background);
}
Here's the result:
I'll leave updating binding appropriately as an exircise.
Upvotes: 1