Joe
Joe

Reputation: 6796

Convert any old WPF Geometry to PathFigureCollection?

I have a series of shape-related view-model classes. Your typical traditional hierarchy: Base ShapeVm class, then LineVm, CircleVm, etc. Each exposes a Geometry property of the appropriate type.

public abstract class ShapeVm
{
    public abstract Geometry Geometry { get; }
}
public class LineVm : ShapeVm
{
    public override Geometry Geometry => new LineGeometry(P1, P2);
    public Point  P1 { get; set; }
    public Point  P2 { get; set; }
}

Until now I've exposed these in XAML using the same type, Path, regardless of the shape type.

<Path Fill="Yellow" StrokeThickness="3" Data={Binding Geometry}"/>

But now I want to be able to apply a transform to each PathGeometry in XAML. That is causing me problems of a "making-my-code-smell" nature:

My approach is to to manually construct my Path object's PathGeometry in XAML and set its Transform property. But I cannot figure out how to cleanly make all the various Geometry types (LineGeometry, EllipseGeometry, etc) easily convert to PathGeometry. I need to supply a "Figures" property to the PathGeometry.

<Path Fill="Yellow" Stroke="Blue" StrokeThickness="3">
    <Path.Data>
        <PathGeometry Figures="???" **** WHAT DO I USE HERE??? ***
                      Transform="{Binding MyViewGeoTransform}"/>
    </Path.Data>
</Path>

The PathGeometry.Figures property is of type PathFigureCollection. Is there a built-in converter somewhere that converts any old WPF Geometry to PathFigureCollection? System.Windows.Media.GeometryConverter does not appear to do the trick.

Is there some obvious easy way to do this that I am missing? Should I just write my converter?

(I realize I could just write a different data template for each shape type and will do that if it's cleanest but using just one Path object in XAML appeals to my sense of simplicity)

Upvotes: 0

Views: 585

Answers (1)

Clemens
Clemens

Reputation: 128062

The most simple approach to a apply a Transform to the Path would perhaps be to set its RenderTransform property:

<Path Fill="Yellow" StrokeThickness="3"
      Data="{Binding Geometry}"
      RenderTransformOrigin="0.5,0.5"
      RenderTransform="{Binding MyViewGeoTransform}"/>

If that is for any reason not desirable, you could use a MultiBinding with a converter that creates a transformed GeometryGroup:

<Path Fill="Yellow" StrokeThickness="3">
    <Path.Data>
        <MultiBinding Converter="{StaticResource TransformedGeometryConverter}">
            <Binding Path="Geometry"/>
            <Binding Path="MyViewGeoTransform"/>
        </MultiBinding>
    </Path.Data>
</Path>

The converter:

public class TransformedGeometryConverter : IMultiValueConverter
{
    public object Convert(
        object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var geometry = new GeometryGroup { Transform = (Transform)values[1] };
        geometry.Children.Add((Geometry)values[0]);
        return geometry;
    }

    public object[] ConvertBack(
        object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Upvotes: 1

Related Questions