Peter McGrattan’s Weblog

Silverlight, WCF, ASP.NET, AJAX, Graphics, RIA

Silverlight 2 Pie Chart (Beta) Updated

Posted by petermcg on August 10, 2008

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

(Code download)

I recently returned from a break and had some time to add a few minor features to the pie chart beta.  If you have any ideas for features not included in this update, now would be a good time to let me know.

IsDoughnut Property and GapScale

One comment recently requested the ability to create a doughnut chart in Silverlight Beta 2.  This functionality has now been added and can be switched on or off via the IsDoughnut property.  The size of the hole in relation to the size of the pie-chart can also be controlled via the GapScale property:

<charts:PieChart x:Name="basicPie" Center="150.0, 150.0" Radius="100.0" IsDoughnut="True" GapScale="0.25"
                 Stroke="LightGray" StrokeThickness="2.0" FontSize="10">
    <charts:PieSegment Percentage="35" Foreground="Orange" />
    <charts:PieSegment Degrees="38" Foreground="Blue" />
    <charts:PieSegment Degrees="72" Foreground="Green" />
    <charts:PieSegment Degrees="101" Foreground="Red" />
    <charts:PieSegment Degrees="23" Foreground="Purple" />
</charts:PieChart>

This produces a chart as follows :

Basic Doughnut Pie Chart

When the IsDoughnut property is set to true, each segment is now comprised of four Points and two ArcSegments:

// Segment Geometry
double pieRadius = this.pieChart.Radius;
double gapRadius = pieRadius * ((this.pieChart.GapScale == 0.0) ? 0.35 : this.pieChart.GapScale);

Point A = GetCircumferencePoint(startAngle, pieRadius);
Point B = (this.pieChart.IsDoughnut) ? GetCircumferencePoint(startAngle, gapRadius) : this.pieChart.Center;
Point C = GetCircumferencePoint(endAngle, gapRadius);
Point D = GetCircumferencePoint(endAngle, pieRadius);

bool isReflexAngle = Math.Abs(endAngle - startAngle) > 180.0;

PathSegmentCollection segments = new PathSegmentCollection();
segments.Add(new LineSegment() { Point = B });

if (this.pieChart.IsDoughnut)
{
    segments.Add(new ArcSegment()
    {
        Size = new Size(gapRadius, gapRadius),
        Point = C,
        SweepDirection = SweepDirection.Clockwise,
        IsLargeArc = isReflexAngle
    });
}

segments.Add(new LineSegment() { Point = D });
segments.Add(new ArcSegment()
{
    Size = new Size(pieRadius, pieRadius),
    Point = A,
    SweepDirection = SweepDirection.Counterclockwise,
    IsLargeArc = isReflexAngle
});

Path segmentPath = new Path()
{
    StrokeLineJoin = PenLineJoin.Round,
    Stroke = this.pieChart.Stroke,
    StrokeThickness = this.pieChart.StrokeThickness,
    Data = new PathGeometry()
    {
        Figures = new PathFigureCollection()
        {
            new PathFigure()
            {
                IsClosed = true,
                StartPoint = A,
                Segments = segments
            }
        }
    }
};

The code above shows how a 2D Path (segmentPath) object is assembled for either a single pie-chart segment or a single doughnut segment.  A close look reveals four Point objects named A,B,C,D and two ArcSegment objects, one drawn in a Clockwise direction and the other in the default CounterClockwise direction.  The following image illustrates the location of these Points and the two ArcSegment objects when the IsDoughnut property is set to true.

Doughnut Arc Points

For a normal pie-chart segment (the default or when IsDoughnut is false) examination of the code above reveals that the Clockwise ArcSegment ending in point C is not required and that Point B becomes the center of the pie-chart.

Label Location, Color and Visibility

The labels can now be displayed inside or outside of the segments and their color and visibility can be controlled via the LabelLocation, LabelStroke and LabelVisibility properties respectively :

<charts:PieChart x:Name="linearPie" Center="150.0,150.0" Radius="100.0" Grid.Row="1"
                 Stroke="LightGray" StrokeThickness="2.0" FontSize="10" LabelLocation="Inside">
    <charts:PieSegment Degrees="45" Foreground="{StaticResource TwoColorLinearBrush}" />
    <charts:PieSegment Percentage="20" Foreground="{StaticResource TwoColorLinearBrush}" />
    <charts:PieSegment Degrees="225.0" Foreground="{StaticResource TwoColorLinearBrush}" />
    <charts:PieSegment Percentage="5" Foreground="{StaticResource TwoColorLinearBrush}" />
</charts:PieChart>

This produces the following chart :

Labels Inside

There have also been a few smaller updates in areas such as the MouseEnter animation and the addition of extra buttons to the demo to demonstrate changing values at runtime.  If you have an idea you would like to see in a future beta of this control, please let me know.

Advertisements

6 Responses to “Silverlight 2 Pie Chart (Beta) Updated”

  1. David Roh said

    Hi Peter,

    Very Nice – thank you for sharing!

    David Roh

  2. […] distinguishing SL assemblies from .NET framework ones, and then expands on how it’s accomplished. Silverlight 2 Pie Chart (Beta) Updated Peter McGrattan has enhanced his Pie Chart control to include a donut chart, and has great pictures […]

  3. zeus said

    amazing code…thank you so much…this has given me clearer understanding of lots of things. Do you think you can show some sample code on how to find points on the Arc…for example if i want to place object on one point of the arc.

  4. Peter McGrattan said

    Hi Zeus,

    Thanks for your comments, glad to have been of some assistance.

    You can find some sample code that shows how to find a point on the arc as part of the code download. Have a look in PieLayoutManager.cs at the code in the LayoutSegment method. Under the ‘// Segment Label’ comment you will see the code that positions the text label for each segment. The method used is GetCircumferencePoint with either inRadius or outRadius as the second parameter, if you change this to pass in this.pieChart.Radius the Point returned will be the mid-point on the outer arc, gapRadius for the inner arc. This not only works for finding the mid-point: instead of using midAngle as the first parameter, any angle between startAngle and endAngle using the appropriate radius will return a Point on the arc at that angle.

    Regards,

    Peter

  5. Richard Clarke said

    Hi Peter

    I’ve been tinkering with the pie chart and came across the following problem. I was attempting to dynamically resize the pie segments programatically, having first created the pie chart in the xaml. I was using the following code:

    linearPie.Segments[0].Percentage = Convert.ToDouble(pct);
    linearPie.Segments[1].Percentage = 100 – Convert.ToDouble(pct);

    But no matter what other piechart methods I called following that, the pie chart display did not refresh. In the end I had to delete the segments and add two new ones to get the pie chart to redraw itself. I am assuming this is just my lack of knowledge and there would have been an easier way to do this – any chance you could explain where I went wrong. I was trying to trigger the OnDependencyPropertyChanged method but it appears setting the percentage doesn’t do this. I am quite new to silverlight and .Net 3.

    Many thanks in advance.

  6. Peter McGrattan said

    Hi Richard,

    Thanks for the feedback: good question. All properties on the PieChart class are dependency properties, the pie-chart will redraw when any of these properties change and OnDependencyPropertyChanged is called. All properties on the PieSegment class including Percentage are not dependency properties and therefore (as you suspected) changing their value at runtime does not cause the pie-chart to redraw.

    I did at one point have the properties on the PieSegment class as dependency properties and may do again but the problem here is that when you change the value of the Percentage property (for example) of an individual PieSegment already added to a PieChart at runtime you are affecting all the other PieSegments in the same collection. In general use the PieSegment object whose Percentage property you are updating at runtime will be part of a collection that already represents 100%. When updating an individual PieSegment’s Percentage property to be greater (for example) you will encroach on another pie-segment’s area and go over the 100% if the chart is re-drawn after every change to the Percentage property.

    In the current implementation the pattern to use when attempting to dynamically resize the pie segments programatically is to build up a new PieSegmentList and when you are happy with it assign it to the Segments dependency property on the PieChart class which will cause a redraw as happened when you deleted and added two new segments. You can see an example of this in the DemoHelper.NextBasicSegments() method in the code download.

    I know this is an area that could do with some improvement and have a few ideas for when I get some time to update the sample. For now hopefully this helps explain what’s happening in the current implementation, you can read more about dependency properties in Silverlight 2 here

    Regards,

    Peter

Sorry, the comment form is closed at this time.

 
%d bloggers like this: