Question

I want to create a own Control with the form of a pie without the tip of it like in the picture afterwards. I just dont get how to get this working.
http://www.directupload.net/file/d/3563/a3hvpodw_png.htm

//EDIT:
Ok I forgot to mention that i want to fill it afterwards. So if I'm right I need a region for that but i don't know how to do this. And to be honest i didn't think about your idea so far. I just used a Pie so far, like this:

Graphics gfx = pe.Graphics;         
Pen p = new Pen(Color.Red);
gfx.DrawPie(p, 0, 0, 200, 200, 0, 45);
base.OnPaint(pe);

It's my first time with custom controls, so sorry if it is a little bit goofy what I'm asking.

Was it helpful?

Solution

Try this:

class ShapedControl : Control
{
    private float startAngle;
    private float sweepAngle;
    private float innerRadius;
    private float outerRadius;

    public ShapedControl()
    {
        InnerRadius = 30;
        OuterRadius = 60;
        StartAngle = 0;
        SweepAngle = 360;
    }

    [DefaultValue(0)]
    [Description("The starting angle for the pie section, measured in degrees clockwise from the X-axis.")]
    public float StartAngle
    {
        get { return startAngle; }
        set
        {
            startAngle = value;
            Invalidate();
        }
    }

    [DefaultValue(360)]
    [Description("The angle between StartAngle and the end of the pie section, measured in degrees clockwise from the X-axis.")]
    public float SweepAngle
    {
        get { return sweepAngle; }
        set
        {
            sweepAngle = value;
            Invalidate();
        }
    }

    [DefaultValue(20)]
    [Description("Inner radius of the excluded inner area of the pie")]
    public float InnerRadius
    {
        get { return innerRadius; }
        set
        {
            innerRadius = value;
            Invalidate();
        }
    }

    [DefaultValue(30)]
    [Description("Outer radius of the pie")]
    public float OuterRadius
    {
        get { return outerRadius; }
        set
        {
            outerRadius = value;
            Invalidate();
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        Graphics g = e.Graphics;
        g.Clear(this.BackColor);

        GraphicsPath gp1 = new GraphicsPath();
        GraphicsPath gp2 = new GraphicsPath();

        float xInnerPos = -innerRadius / 2f + this.Width / 2f;
        float yInnerPos = -innerRadius / 2f + this.Height / 2f;
        float xOuterPos = -outerRadius / 2f + this.Width / 2f;
        float yOuterPos = -outerRadius / 2f + this.Height / 2f;

        if (innerRadius != 0.0)
            gp1.AddPie(xInnerPos, yInnerPos, innerRadius, innerRadius, startAngle, sweepAngle);
        gp2.AddPie(xOuterPos, yOuterPos, outerRadius, outerRadius, startAngle, sweepAngle);

        Region rg1 = new System.Drawing.Region(gp1);
        Region rg2 = new System.Drawing.Region(gp2);

        g.DrawPath(Pens.Transparent, gp1);
        g.DrawPath(Pens.Transparent, gp2);

        rg1.Xor(rg2);

        g.FillRegion(Brushes.Black, rg1);

        this.Region = rg1;

    }

    //Just for testing purpose. Place a breakpoint
    //in here and you'll see it will only get called when
    //you click inside the "pie" shape
    protected override void OnClick(EventArgs e)
    {
        base.OnClick(e);
    }
}

EDIT: made the code better by centering the shape and adding properties for the VS Designer , stolen from another answer ;-)

MORE EDITS: taking care of the case where Inner Radius == 0

OTHER TIPS

Try this code sample of a complex shaped control.
You can control its shape using StartAngle, SweepAngle and InnerPercent properties.

public partial class PathUserControl : UserControl
{
    private readonly GraphicsPath outerPath = new GraphicsPath();
    private readonly GraphicsPath innerPath = new GraphicsPath();
    private float startAngle;
    private float sweepAngle = 60;
    private float innerPercent = 30;

    public PathUserControl()
    {
        base.BackColor = SystemColors.ControlDark;
    }

    [DefaultValue(0)]
    [Description("The starting angle for the pie section, measured in degrees clockwise from the X-axis.")]
    public float StartAngle
    {
        get { return startAngle; }
        set
        {
            startAngle = value;
            SetRegion();
        }
    }

    [DefaultValue(60)]
    [Description("The angle between StartAngle and the end of the pie section, measured in degrees clockwise from the X-axis.")]
    public float SweepAngle
    {
        get { return sweepAngle; }
        set
        {
            sweepAngle = value;
            SetRegion();
        }
    }

    [DefaultValue(30)]
    [Description("Percent of the radius of the excluded inner area of the pie, measured from 0 to 100.")]
    public float InnerPercent
    {
        get { return innerPercent; }
        set
        {
            if (value < 0 || value > 100f)
                throw new ArgumentOutOfRangeException("value", "Percent must be in the range 0 .. 100");
            innerPercent = value;
            SetRegion();
        }
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        SetRegion();
    }

    private void SetRegion()
    {
        if (Region != null)
        {
            Region.Dispose();
            Region = null;
        }

        if (ClientSize.IsEmpty)
            return;

        float innerCoef = 0.01f * InnerPercent;

        outerPath.Reset();
        innerPath.Reset();
        outerPath.AddPie(0, 0, ClientSize.Width, ClientSize.Height, StartAngle, SweepAngle);
        innerPath.AddPie(ClientSize.Width * (1 - innerCoef) / 2, ClientSize.Height * (1 - innerCoef) / 2, ClientSize.Width * innerCoef, ClientSize.Height * innerCoef, StartAngle, SweepAngle);

        Region region = new Region(outerPath);
        region.Xor(innerPath);

        Region = region;
    }
}

EDIT The @LucMorin idea with XOR is great, I've stolen it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top