WPF creates a ring chart with details and a wpf ring chart

Effect

Details are implemented by using Popup. When recording gif images, the Popup cannot be displayed and I don't know why. So let's take a look at the static diagram.

General idea

Use Arc + Popup for chart implementation

The chart is divided into two parts: one is the ring part and the other is the detailed part of the annotation.

The ring is represented in Arc graphs. note that this Arc is a Blend image. if you use Blend to create a project, you can directly use it. To create a project using VS, you must add a reference Microsoft. expression. drawing extension under reference manager => Assembly => (provided that Blend has been installed)

The details section uses the Popup control. The IsOpen attribute is bound to the IsMouseOver of the Arc, that is, when the mouse enters the Arc, the Popup will be displayed.

Popup contains an elliptical control as the background, a text display, and a line dotted as a pointer.

Then, Popup is positioned to the appropriate position of the corresponding arc to display (here the middle of the arc)

Sorry, the style is ugly. Ignore it. Focus on positioning.

Arc Section

Arc has two important attributes: StartAngle and EndAngle, which determine the proportion of the Arc to the ring.

Each data item corresponds to an arc. All the arcs are placed in a container and connected at the beginning and end.

If the total number of data items is 100, all arcs form a complete ring.

Popup details

The details are divided into four types, as shown in the figure below.

Elliptic

As shown in the figure, the background elliptic is divided into two types: less than 180 degrees. The elliptic is aligned by the right side of the container, greater than 180 degrees, and by the left side of the container.

That is, this part of the Code:

Ellipse ell = new Ellipse () {Fill = brush}; // the center point angle is less than 180. The details are displayed on the right. Otherwise, the Grid detailGrid = new Grid () {Width = _ popupHeight is displayed on the left, horizontalAlignment = HorizontalAlignment. right}; if (middleAngle> 180) {detailGrid. horizontalAlignment = HorizontalAlignment. left ;}

Line

Line is divided into four types, each angle interval corresponds to one type

private Polyline GetPopupPolyline(double middleAngle){ Polyline pLine = new Polyline() { Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)), StrokeDashArray = new DoubleCollection(new double[] { 5, 2 }) }; double x1 = 0, y1 = 0; double x2 = 0, y2 = 0; double x3 = 0, y3 = 0; if (middleAngle > 0 && middleAngle <= 90) { x1 = 0; y1 = _popupHeight; x2 = _popupWidth / 2; y2 = _popupHeight; x3 = _popupWidth * 3 / 4; y3 = _popupHeight / 2; } if (middleAngle > 90 && middleAngle <= 180) { x1 = 0; y1 = 0; x2 = _popupWidth / 2; y2 = 0; x3 = _popupWidth * 3 / 4; y3 = _popupHeight / 2; } if (middleAngle > 180 && middleAngle <= 270) { x1 = _popupWidth; y1 = 0; x2 = _popupWidth / 2; y2 = 0; x3 = _popupWidth / 4; y3 = _popupHeight / 2; } if (middleAngle > 270 && middleAngle <= 360) { x1 = _popupWidth; y1 = _popupHeight; x2 = _popupWidth / 2; y2 = _popupHeight; x3 = _popupWidth / 4; y3 = _popupHeight / 2; } pLine.Points.Add(new Point(x1, y1)); pLine.Points.Add(new Point(x2, y2)); pLine.Points.Add(new Point(x3, y3)); return pLine;}

Popup Positioning

First, take 0-90 degrees as an example to describe some basic things. See figure

First, the default position of Popup is in the lower left of its container. The upper left corner of Popup overlaps with the lower left corner of the container.

What we need to do now is to mark the position of Popup as a red point, and coincide with the position marked as a red point on the ring.

Let's review the formulas we learned when we were a child:

1. Right Triangle a = r * sinA

2. Hook Theorem c ^ 2 = a ^ 2 + B ^ 2 B = Sqrt (c ^ 2-a ^ 2)

Right Triangle. The opposite side of corner A is a, the adjacent side is B, and the oblique side is c. obviously, the radius r of the c side in the circle is equal. note: Because the arc has a thickness, the arc thickness of 1/2 must be subtracted from the r.

Angle A can be obtained from the angle corresponding to the arc minus 90 degrees, that is, the sinA value is known, then the length of a and B can be obtained, then you can move the Popup.

1. 0-90 degrees

X axis: 1. Move the width of 1/2 containers to the right. 2. Move a B distance to the right.

Y axis: 1. Move the height of 1/2 containers up 2. Move the height of a Popup up 3. Move the distance of

Ii. 90-180 degrees

X axis: 1. Move the width of 1/2 containers to the right. 2. Move the distance to the right.

Y axis: 1. Move up the Thickness of 1/2 arcs to ensure that the starting point of the mark is in the center of the arc. 2. Move up the distance of one (r-B ).

Iii.-degrees

X axis: 1. Move a B distance to the left

Y axis: 1. Move up the Thickness of the 1/2 arc to ensure that the starting point of the mark is in the center of the arc. 2. Move up the distance of one (r-).

Iv. 270-360 degrees

X axis: 1. Move a distance to the left

Y axis: 1. Move up the height of 1/2 containers 2. Move up a Popup height 3. Move up the distance of B

Code Section

Private Popup GetPopup (double middleAngle) {/** generate popup * set the offset of popup so that the starting point of the marking line corresponds to the center point of the arc */Popup popup = new Popup () {Width = _ popupWidth, Height = _ popupHeight, AllowsTransparency = true, IsHitTestVisible = false }; // Cartesian Triangle a = r * sinA ing Theorem c ^ 2 = a ^ 2 + B ^ 2 B = Sqrt (c ^ 2-a ^ 2) double r = _ chartWidth/2-_ arcThickness/2; double offsetX = 0, offsetY = 0; if (middleAngle> 0 & middleAngle <= 90) {double sinA = Math. sin (Math. PI * (90-middleangle)/180); double a = r * sinA; double c = r; double B = Math. sqrt (c * c-a * a); offsetX = _ chartWidth/2 + B; offsetY =-(_ chartWidth/2 + _ popupHeight + );} if (middleAngle> 90 & middleAngle <= 180) {double sinA = Math. sin (Math. PI * (180-middleAngle)/180); double a = r * sinA; double c = r; double B = Math. sqrt (c * c-a * a); offsetX = _ chartWidth/2 + a; offsetY =-(_ arcThickness/2 + (r-B ));} if (middleAngle> 180 & middleAngle <= 270) {double sinA = Math. sin (Math. PI * (270-middleAngle)/180); double a = r * sinA; double c = r; double B = Math. sqrt (c * c-a * a); offsetX =-B; offsetY =-(_ arcThickness/2 + (r-));} if (middleAngle> 270 & middleAngle <= 360) {double sinA = Math. sin (Math. PI * (360-middleAngle)/180); double a = r * sinA; double c = r; double B = Math. sqrt (c * c-a * a); offsetX =-a; offsetY =-(_ chartWidth/2 + _ popupHeight + B);} popup. horizontalOffset = offsetX; popup. verticalOffset = offsetY; return popup ;}

This is almost the main thing. It's a little tiring to draw a picture here.

Source code download: ArcChart.zip