Question

I am trying to create a custom wedge shape class in WPF, derived from the abstract Shape class, and be able to define it in XAML just like any other shape.

I've been searching on Google for a complete tutorial on how to do this but all I am finding is stuff on custom controls. What I want is to create a wedge class that allows me to specify inner radius, outer radius, how many sections out of 360 degrees this wedge will be one of (i.e., if I want to fit 24 of these wedges around the circle, this wedge will be the right size to be one of those 24), and its position (which one of those 24 spaces it will occupy). These are all dependency properties, and I've registered them.

The DefiningGeometry property calls a method that does all the logic for calculating points and drawing the shape.

The problem I'm running into is that VS2010 created a style automatically with a setter of property "Template". Then, when I compile, it gives me an error saying:

"Error 3 Cannot find the Style Property 'Template' on the type 'WpfApplication1.Wedge'. Line 8 Position 17. C:\Users\rflint\Desktop\WpfApplication1\WpfApplication1\Themes\Generic.xaml 8 17 WpfApplication1"

If I comment this out everything compiles but the wedge is not shown on the form. How do I implement this Template setter property? Do I need to?

XAML:

    <my:Wedge CenterPoint="300,300" InnerRadius="100" OuterRadius="200" Sections="12" Position="0" Stroke="Transparent" Fill="#FFCC7329" />

C#:

protected override Geometry DefiningGeometry
{
  get
  {
    using (StreamGeometryContext context = geometry.Open())
    {
      DrawWedgeGeometry(context);
    }

    return geometry;
  }
}

        private void DrawWedgeGeometry(StreamGeometryContext context)
    {
        double wedgeAngle = 360/Sections;
        double angleA = (Position * wedgeAngle) + (wedgeAngle/2);
        double angleB = (Position * wedgeAngle) - (wedgeAngle/2);
        Point point1 = getPointOnCircle(CenterPoint, InnerRadius, angleA);
        Point point2 = getPointOnCircle(CenterPoint, InnerRadius, angleB);
        Point point3 = getPointOnCircle(CenterPoint, OuterRadius, angleB);
        Point point4 = getPointOnCircle(CenterPoint, OuterRadius, angleA);

        Size innerSize = new Size(InnerRadius, InnerRadius);
        Size outerSize = new Size(OuterRadius, OuterRadius);

        context.BeginFigure(point1, true, true);
        context.ArcTo(point2, innerSize, 90, false, SweepDirection.Clockwise, true, true);
        context.LineTo(point3, true, true);
        context.ArcTo(point4, outerSize, 90, false, SweepDirection.Counterclockwise, true, true);
    }
Was it helpful?

Solution

I've just tried it on VS2012 and it works fine, at least with a simple ellipse geometry:

public sealed class Wedge : Shape
{
    public Double Radius
    {
        get { return (Double)this.GetValue(RadiusProperty); }
        set { this.SetValue(RadiusProperty, value); }
    }
    public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register(
      "Radius", typeof(Double), typeof(Wedge), new PropertyMetadata(0.0));

    protected override Geometry DefiningGeometry
    {
        get {return new EllipseGeometry(new Point(0, 0), this.Radius, this.Radius); }
    }
}

And the XAML:

<local:Wedge Radius="50" Stroke="Black" Fill="Yellow" StrokeThickness="2" Canvas.Top="100" Canvas.Left="100" />

OTHER TIPS

Let me give a simple solution to your problem:

public class Wedge : Shape
{
    public Double StartAngle
    {
        get { return (Double)GetValue(StartAngleProperty); }
        set { SetValue(StartAngleProperty, value); }
    }

    public static readonly DependencyProperty StartAngleProperty =
        DependencyProperty.Register("StartAngle", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));


    public Double EndAngle
    {
        get { return (Double)GetValue(EndAngleProperty); }
        set { SetValue(EndAngleProperty, value); }
    }

    public static readonly DependencyProperty EndAngleProperty =
        DependencyProperty.Register("EndAngle", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));


    public Point Center
    {
        get { return (Point)GetValue(CenterProperty); }
        set { SetValue(CenterProperty, value); }
    }

    public static readonly DependencyProperty CenterProperty =
        DependencyProperty.Register("Center", typeof(Point), typeof(Wedge), new PropertyMetadata(new Point()));



    public Double InnerRadius
    {
        get { return (Double)GetValue(InnerRadiusProperty); }
        set { SetValue(InnerRadiusProperty, value); }
    }

    public static readonly DependencyProperty InnerRadiusProperty =
        DependencyProperty.Register("InnerRadius", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));

    public Double OuterRadius
    {
        get { return (Double)GetValue(OuterRadiusProperty); }
        set { SetValue(OuterRadiusProperty, value); }
    }

    public static readonly DependencyProperty OuterRadiusProperty =
        DependencyProperty.Register("OuterRadius", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));

    protected override Geometry DefiningGeometry
    {
        get
        {
            StreamGeometry geometry = new StreamGeometry();
            using (StreamGeometryContext context = geometry.Open())
            {
                Draw(context);
            }
            return geometry;
        }
    }

    private void Draw(StreamGeometryContext context)
    {
        var isStroked = Stroke != null & Stroke != Brushes.Transparent & StrokeThickness > 0;
        var isFilled = Fill != null & Fill != Brushes.Transparent;

        context.BeginFigure(
            GetPointOnCircle(Center, OuterRadius, StartAngle),
            isFilled, 
            true);

        context.ArcTo(
            GetPointOnCircle(Center, OuterRadius, EndAngle), 
            new Size(OuterRadius, OuterRadius),
            0,
            EndAngle - StartAngle > 180, 
            SweepDirection.Clockwise,
            isStroked, 
            true);

        context.LineTo(GetPointOnCircle(Center, InnerRadius, EndAngle), isStroked, true);

        context.ArcTo(
            GetPointOnCircle(Center, InnerRadius, StartAngle),
            new Size(InnerRadius, InnerRadius),
            0,
            EndAngle - StartAngle > 180,
            SweepDirection.Counterclockwise,
            isStroked,
            true);

        context.LineTo(GetPointOnCircle(Center, OuterRadius, StartAngle), isStroked, true);
    }

    private Point GetPointOnCircle(Point center, double radius, double angle)
    {
        var px = center.X + radius * Math.Cos(ToRadians(angle));
        var py = center.Y + radius * Math.Sin(ToRadians(angle));
        return new Point(px, py);
    }

    public double ToRadians(double angle)
    {
        return angle * Math.PI / 180;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top