Draw words on a circle in WPF and C#

[draw words]

This example shows one way that you can draw words on a circle in WPF. Note that it does not show how to draw words where each character is rotated to follow the circle’s curve. Each word is rotated as a whole. That generally produces a good result for relatively small words such as the ones drawn here.

The following text shows the program’s control hierarchy created at design time. The text includes some of the controls’ properties to make it easier to identify the controls on the picture.

Window
    Grid Background="LightGreen"
        Label Name="lblZoom" Content="100%"
        Slider Name="sliZoom"
        ScrollViewer Name="scvGraph"
            Canvas Name="canGraph" Background="White"

When you adjust the slider, the following code adjusts the program’s zoom level.

// Zoom.
private double Zoom = 1;
private void sliZoom_ValueChanged(object sender,
    RoutedPropertyChangedEventArgs e)
{
    // Make sure the control's are all ready.
    if (!IsInitialized) return;

    // Display the zoom factor as a percentage.
    lblZoom.Content = sliZoom.Value + "%";

    // Get the scale factor as a fraction 0.25 - 2.00.
    double scale = (double)(sliZoom.Value / 100.0);

    // Scale the graph.
    canGraph.LayoutTransform = new ScaleTransform(scale, scale);
}

This code checks the window’s IsInitialized property to make sure that all of the controls have been created and are ready to go. It then changes the lblZoom control to display the new zoom level. It calculates a new scale factor and sets the Graph control’s LayoutTransform to scale the Graph control by the scale factor.

That makes the Graph control scale itself and all of its contents. That control is contained in a ScaleViewer, which automatically displays scroll bars if necessary so you can view the whole Graph control. (ScaleViewer is one of my favorite WPF controls.)

The only remaining part of the program is the following code, which that draws the circles and the text along them.

// Draw some circles and text.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Get the Canvas geometry.
    double wid = canGraph.ActualWidth;
    double hgt = canGraph.ActualHeight;
    double cx = wid / 2;
    double cy = hgt / 2;

    // Make some centered circles.
    for (int radius = 200; radius > 0; radius -= 50)
    {
        byte b = (byte)(radius / 2 + 155);
        byte r = (byte)(b / 2);
        byte g = (byte)(b / 2);

        Ellipse ellipse = new Ellipse();
        ellipse.Fill = new SolidColorBrush(
            Color.FromArgb(255, r, g, b));
        ellipse.Stroke = Brushes.Black;
        ellipse.StrokeThickness = 3;
        Canvas.SetLeft(ellipse, cx - radius);
        Canvas.SetTop(ellipse, cy - radius);
        ellipse.Width = 2 * radius;
        ellipse.Height = 2 * radius;
        canGraph.Children.Add(ellipse);
    }

    // Make some rotated text.
    double font_size = 10;
    for (int radius = 25; radius < 200; radius += 50)
    {
        const double num_angles = 6;
        double dtheta = 360 / num_angles;
        double theta = dtheta / 2;
        for (int i = 0; i < num_angles; i++)
        {
            // Convert into a counterclocckwise angle
            // where 0 is to the right.
            double angle = 90 - theta;

            // Math.Sin and Math.Cos use radians.
            double radians = (angle - 90) / 180 * Math.PI;
            double x = cx + radius * Math.Cos(radians);
            double y = cy + radius * Math.Sin(radians);

            // Use theta for the text, not angle.
            string text = ((int)theta).ToString();

            // Draw the text.
            DrawText(canGraph, text,
                new Point(x, y), angle, font_size,
                HorizontalAlignment.Center,
                VerticalAlignment.Center);

            theta += dtheta;
        }
        font_size += 3;
    }
}

The code first uses a loop to make some concentric circles. It makes them from largest to smallest so the later circles sit above the earlier ones. If you made them in smallest-to-largest order, the later circles would hide the earlier ones.

Nxet, the program uses two nested loops to draw the text. The outer loop ranges over the radii at which the program should draw text.

The inner loop ranges over the number of angles that the program needs to draw. This example draws text at six positions around the circle. The angle theta determines the position where the text should be drawn. Shortly the code will use sines and cosines to calculate the text's positions. Because Y coordinates increase downward in WPF (and Windows Forms and most programming languages), the angles used by sines and cosines increase clockwise. To make the text increase counterclockwise, the code subtracts theta from 90 to convert it into angle. It then subtracts 90 degrees and scales the result to convert angle into radians, which is the unit that the Math.Sin and Math.Cos methods require.

The code then calculates the position where the text should be drawn and calls the DrawText method to draw the text. For information on the DrawText method, see the post Draw a graph with rotated text in WPF and C#.

When the inner loop ends, the program increases the font size by 3 points so the text in the outer circles is larger.

[example]

One of the nice things about WPF is that graphics are normally represented by objects, so you can zoom in as far as you like and still get a nice, smooth result. For example, the picture on the right shows the program zoomed in so the circle is shown at 500% of its normal scale. You can see that the edges of the circles and the text 150 are still nice and smooth. Making magnified text appear smooth is harder in Windows Forms applications.

Download the example solution to see additional details and to experiment with the program. For example, you might try to scale the text so the program draws multiples of 10 between 0 and 100 instead of displaying angles in degrees.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, graphics, mathematics and tagged , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.