Subclassing Shape (or more accurately, Path)

One of the more subtle improvements in Silverlight 4 is that we unsealed the Path class. That means you can write a class that behaves like a shape without re-implementing properties like Fill, Stroke, etc. -- just subclass, set Path.Data to render what you want, and provide Measure/Arrange appropriate functionality. (Existing Shapes don't all use the same measure/arrange logic)  Here's a sample Triangle class:

    public class Triangle : Path
    {
        public Triangle()
        {
            CreatePathData(0,0);
        }

        private double lastWidth = 0;
        private double lastHeight = 0;
        private PathFigure figure;

        private void AddPoint(double x, double y)
        {
            LineSegment segment = new LineSegment();
            segment.Point = new Point(x + 0.5 * StrokeThickness,
                y + 0.5 * StrokeThickness);
            figure.Segments.Add(segment);
        }

        private void CreatePathData(double width, double height)
        {
            // in order to fit in our layout slot we need to reduce the size of the stroke
            height -= this.StrokeThickness;
            width -= this.StrokeThickness;

            // needed to avoid a layout loop
            if (lastWidth == width && lastHeight == height) return;
            lastWidth = width;
            lastHeight = height;

            var geometry = new PathGeometry();
            figure = new PathFigure();
            figure.StartPoint = new Point(0 + 0.5 * StrokeThickness, height + 0.5 * StrokeThickness);
            AddPoint(width, height);
            AddPoint(width / 2, 0);
            AddPoint(0, height);
            figure.IsClosed = true;
            geometry.Figures.Add(figure);
            this.Data = geometry;
        }
       
        protected override Size MeasureOverride(Size availableSize)
        {
            return availableSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            CreatePathData(finalSize.Width, finalSize.Height);
            return finalSize;
        }
    }

Enjoy!

Comments

  • Anonymous
    February 05, 2010
    Thanks for the tip. I was just getting into doing custom shapes in WPF and wanted to port the code over to Silverlight only to hit a bunch of limitations. I would have preferred to inherit from Geometry to create a custom geometry and then implement a shape that uses this new geometry but the Shape class in Silverlight does not allow you to override the protected DefiningGeometry property so it all went to waste.

  • Anonymous
    February 11, 2010
    "the Shape class in Silverlight does not allow you to override the protected DefiningGeometry property" I still haven't found a good reason for this...  Anyone?

  • Anonymous
    February 12, 2010
    Also, there doesn't seem to be a "Data" property for Path... :(