Click here to Skip to main content
Click here to Skip to main content

CircleControl - A Circular Motion Control

By , 8 Sep 2012
 

Introduction

This is release 1.2 of the library. It adds the SnapMode and SnapAngles properties to the CircleControl.MarkerSet class.

The CircleControl class is a .NET 2.0 control which allows circular motion, for example, rotating a dial, or setting the hands on an analog clock.

The control supports markers - graphical objects which can be positioned programmatically and/or dragged with the mouse, and marker sets, which are groups of markers which move in unison. The background may be populated with colored rings, tick marks, and text strings.

All public and protected classes, methods and properties are fully documented using standard C# XML documentation comments. The project includes an HTML help file. Refer to the 'Overview' section in the help file for more details on using the class, and refer to the 'Sample Program' section for a complete source listing of a working application.

The CircleControl_12_Library download includes:

CircleControl.dll Class library
CircleControl.chm Help file.

The CircleControl_12_Demo download includes the above files, as well as:

AnalogClock.exe Sample program showing an analog clock; the time may be changed by dragging the hour and minute hands.
ColorDialer.exe Sample program for picking a color; rotate the red, blue, and green dials to select the RGB values. The program is visually gratuitous demonstration of the control, and is not meant to be a serious color picker.
EditDefaults.exe Sample program demonstrating the effect of changing various CircleControl properties.
GantryControl.exe Sample program as detailed in the Sample Program section of the help file.
ManyRings.exe Sample program showing excessive use of rings. Includes capability to compare paint times with FixedBackground set to true or false.

The CircleControl_12_Source download includes the source for all of the above programs, as well as the necessary files for building the help file.

Using the Code

To use the CircleControl class, simply add it on an existing form:

CircleControl cc = new CircleControl();
cc.Location = new System.Drawing.Point(0, 0);
cc.Size = new System.Drawing.Size(200, 200);
form.Controls.Add(cc);

By default, a new instance of CircleControl comes ready to use with a single triangular marker and ten tick marks.

To add a new marker, create a new MarkerSet, and add one or more Marker objects:

CircleControl.MarkerSet ms = new CircleControl.MarkerSet();
cc.MarkerSets.Add(ms);

// Polygon which defines shape of marker
PointF[] poly = new PointF[4];
poly[0] = new PointF(0.25F,  0.00F);
poly[1] = new PointF(0.70F,  0.18F);
poly[2] = new PointF(0.64F,  0.00F);
poly[3] = new PointF(0.70F, -0.18F);

CircleControl.Marker m = new CircleControl.Marker(
                      Color.Brown,       // inside color
                      Color.DarkGreen,   // border color
                      1.0f,              // border thickness
                      poly,              // polygon defining marker shape
                      130.0f,            // angle offset of marker
                      MouseButtons.Left, // which button(s) can drag the marker
                      true);             // is marker visible?

// Add new marker to MarkerSet, at which point
// it becomes visible on the control
ms.Add(m);

The polygon defines the appearance of the marker at angle zero. It uses a cartesian coordinate system, where (0,0) is the center of the control, and 1.0 is the distance to the nearest edge. The internal area of a marker can be a solid color, a hatch pattern, or a variety of color gradients. Borders can be of any color and thickness.

An AngleChanged event is raised whenever the angle of a marker changes, or the mouse state of a dragged marker changes. To receive events, install a handler:

cc.AngleChanged += new CircleControl.AngleChangedHandler(OnAngleChange);

The background may be populated with colored rings, tick marks, and text strings. The following code snippet adds a beige-colored ring, and four text items, as they would be placed on a compass:

cc.Rings.Add(new CircleControl.Ring(0.6f,        // size as radius
                                    Color.Beige, // internal color
                                    Color.Black, // border color
                                    2.0f);       // border thickness

Font f = new Font("Arial", 8.0f);
cc.TextItems.Add(new CircleControl.TextItem(f,         // font
                                            Color.Red, // color
                                            "N",       // text
                                            0.8f,      // distance from origin
                                            90.0f);    // angle
cc.TextItems.Add(new CircleControl.TextItem(f, Color.Red, "S", 0.8f, 270.0f);
cc.TextItems.Add(new CircleControl.TextItem(f, Color.Red, "E", 0.8f,   0.0f);
cc.TextItems.Add(new CircleControl.TextItem(f, Color.Red, "W", 0.8f, 180.0f);

The internal area of a ring can be a solid color, a hatch pattern, or a variety of color gradients. Borders can be of any color and thickness. Text items can be of any font, color, or rotation. They can be a fixed size, or made to be relative to the size of the control.

The above code snippets only provide a brief overview. Refer to the project help file for complete class documentation.

Points of Interest

Writing, refining, and debugging the code was, as always, a joyful experience. But documenting every public and protected class, method and property was not. It was drudgery. The pain of writing full and proper documentation was offset in part by Sandcastle Help File Builder, a freely available tool used to generate the help file. But documenting everything was a learning experience, and I've developed immense respect for programmers who write complete and useful documentation, especially for those at Microsoft who are responsible for the creation of the extensive .NET documentation.

History

  • September 8, 2012 - Release 1.2
    • Added SnapMode and SnapAngles properties CircleControl.MarkerSet.
  • July 21, 2011 - Release 1.1.2
    • Fixed bug in CircleControl.Collections.Insert() method where the Cc parameter was improperly being set to null
  • November 1, 2010 - Release 1.1.1
    • Fixed bug in CircleControl.SetAngleMinMax() method where the call was ignored unless both min and max parameters were different from the current values
    • Fixed bug where marker angles were not always calculated properly if AngleWraps was false
  • October 12, 2010 - Release 1.1
    • Added FixedBackground property to CircleControl.
  • September 12, 2010 - First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Graham Wilson
Software Developer (Senior)
Canada Canada
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberKD0YU30 Nov '12 - 3:13 
5 from me also.
The author clearly had a mental grasp on what was needed and what was not. Very well documented also.
QuestionCoolmembersam.hill8 Sep '12 - 16:27 
5 from me.
QuestionInvert Primary MarkermemberTodd Denlinger8 Aug '12 - 12:07 
Excellent Control! Thanks so much for sharing.
 
I was wondering if you could tell me how to invert the primary marker: that is, what array of points would I use to have the arrow pointing away from the center of the control as opposed to pointing towards the center of the control?
AnswerRe: Invert Primary MarkermemberGraham Wilson8 Aug '12 - 15:58 
Todd,
 
The default marker is built using this code inside the control:
PointF[] markerPoly = new PointF[4];
markerPoly[0] = new PointF(0.35F,  0.00F);
markerPoly[1] = new PointF(0.80F,  0.20F);
markerPoly[2] = new PointF(0.70F,  0.00F);
markerPoly[3] = new PointF(0.80F, -0.20F);
To make the marker point away from the center, you could just move around the X values. To replace the primary marker, you would first clear the existing markers, and then add your own.
 
This code would work (assuming you have a CircleControl named 'cc'):
cc.MarkerSets.Clear();
PointF[] markerPoly = new PointF[4];
markerPoly[0] = new PointF(0.80F,  0.00F);
markerPoly[1] = new PointF(0.35F,  0.20F);
markerPoly[2] = new PointF(0.45F,  0.00F);
markerPoly[3] = new PointF(0.35F, -0.20F);
CircleControl.Marker m = new CircleControl.Marker(CircleControl.MakeArgb(0.7f, Color.DarkGray),
                                                  Color.Black,
                                                  2.0f,
                                                  markerPoly);
CircleControl.MarkerSet set = new CircleControl.MarkerSet();
set.Add(m);
cc.MarkerSets.Add(set);
I hope this helps!
GeneralRe: Invert Primary MarkermemberTodd Denlinger9 Aug '12 - 4:27 
Great! Thanks much for your help.
GeneralMy vote of 5memberProEnggSoft13 Mar '12 - 1:06 
Excellent article
GeneralMy vote of 4membermariazingzing15 Feb '12 - 12:56 
nice
GeneralMy vote of 5memberpeteSJ22 Jul '11 - 6:57 
Very complete!
Questionexcellent, and fully documented codememberBillWoodruff21 Jul '11 - 12:06 
+5=> Thanks for this great UserControl example, with several visually interesting demo projects, and very thorough documentation, including a 'real,' help file !
 
fyi: no problem converting, compiling, and using, in VS 2010 Pro, .NET 4.0 Client FrameWork.
 
best, Bill
"Reason is the natural order of truth; but imagination is the organ of
meaning." C.S. Lewis

QuestionCoolmemberCIDev21 Jul '11 - 5:22 
Cool stuff. Cool | :cool:
Just because the code works, it doesn't mean that it is good code.

QuestionGood workmemberShahriar Iqbal Chowdhury21 Jul '11 - 4:58 
good work Thumbs Up | :thumbsup: my 5
GeneralMy vote of 5memberashishkumar00831 May '11 - 12:19 
excellent
GeneralMarkers and ringsmemberjrwakeen6 Feb '11 - 17:19 
I'd like a marker that is a circle, maybe with gradient shading. How can I make a circle from an array of PointF?
 
How can I position a marker within a specific ring? I'd like to move a marker to the outermost ring.
GeneralRe: Markers and ringsmemberGraham Wilson8 Feb '11 - 16:29 
(I'll answer each of your questions in a separate response).
 
Your first question - how to make a circular marker using PointF's with gradient shading.
 
Presently, the only way is to calculate an arbitrary number of points on the circle, and enter them into a PointF array. The more points entered, the greater the work, but the smoother the circle.
 
The following code generates a circular marker with a radial color gradient going from red to blue. To use this code, create a Windows Application, and add a CircleControl named cc to the form. Paste the following code into the form's constructor immediately after the call to InitializeComponent().
cc.MarkerSets.Clear();
 
List<PointF> points = new List<PointF>();
points.Add(new PointF( 0.200f,  0.000f));
points.Add(new PointF( 0.220f,  0.100f));
points.Add(new PointF( 0.300f,  0.200f));
points.Add(new PointF( 0.400f,  0.240f));
points.Add(new PointF( 0.500f,  0.240f));
points.Add(new PointF( 0.600f,  0.200f));
points.Add(new PointF( 0.680f,  0.100f));
points.Add(new PointF( 0.700f,  0.000f));
points.Add(new PointF( 0.680f, -0.100f));
points.Add(new PointF( 0.600f, -0.200f));
points.Add(new PointF( 0.500f, -0.240f));
points.Add(new PointF( 0.400f, -0.240f));
points.Add(new PointF( 0.300f, -0.200f));
points.Add(new PointF( 0.220f, -0.100f));
points.Add(new PointF( 0.200f, -0.000f));
 
CircleControl.Marker m = new CircleControl.Marker(
    new PointF(0.200f, 0.000f), // start point of gradient
    new PointF(0.700f, 0.000f), // end point of gradient
    Color.Red,                  // start color
    Color.Blue,                 // end color
    Color.Green, 1.0f,          // border color & thickness
    points.ToArray(),           // points
    0.0f,                       // offset angle in markerset
    MouseButtons.Left,          // mouse buttons to drag
    true);                      // visible

CircleControl.MarkerSet set = new CircleControl.MarkerSet();
set.Add(m);
cc.MarkerSets.Add(set);
 
I am planning a future addition to the class which will allow for Marker's composed of GraphicPath objects, which will allow for a wide variety of shapes. But until then, you must used PointF arrays.
GeneralRe: Markers and ringsmemberGraham Wilson8 Feb '11 - 17:20 
Your second question - how to position a marker within a specific ring.
 
The position of a ring is defined by it's radius and the sum of the radii of previously defined rings. The radial position of a marker is defined by the minimum and maximum x values in the PointF array.
 
To position a marker within a particular ring, the minimum and maximum x values of the points of the marker must fall within the minimum and maximum radii values of the ring.
 
The following code generates a CircleControl with three rings. The first ring (light blue) extends from the center to a radius of 0.3. The second ring (orange) extends from a radius of 0.3 to 0.6, and the final ring (dark green) extends from a radius 0.6 to 1.0. There is single marker (a yellow triangle), which rotates only over the final ring.
 
To use this code, create a Windows Application, and add a CircleControl named cc to the form. Paste the following code into the form's constructor immediately after the call to InitializeComponent().
cc.Rings.Clear();
cc.Rings.Add(0.30f, Color.LightBlue);
cc.Rings.Add(0.30f, Color.Orange);
cc.Rings.Add(0.40f, Color.DarkGreen);
 
cc.MarkerSets.Clear();
 
List<PointF> points = new List<PointF>();
points.Add(new PointF( 0.70f,  0.00f));
points.Add(new PointF( 0.90f,  0.10f));
points.Add(new PointF( 0.90f, -0.10f));
points.Add(new PointF( 0.70f,  0.00f));
 
CircleControl.Marker m = new CircleControl.Marker(
		Color.Yellow,       // inside color
		Color.Black, 1.0f,  // border color & thickness
		points.ToArray(),   // points
		0.0f,               // offset angle in markerset
		MouseButtons.Left); // mouse buttons to drag

CircleControl.MarkerSet set = new CircleControl.MarkerSet();
set.Add(m);
cc.MarkerSets.Add(set);
 
Note - as explained in the class documentation, the length and position of most CircleControl objects is specified relative to the size of the control, where 1.0 is defined as the distance from the center of the control to the nearest edge.
GeneralMy vote of 5memberVistaHacker2 Nov '10 - 4:07 
very nice article! Cool | :cool:
Thumbs Up | :thumbsup:
GeneralMy vote of 5memberRoger Wright1 Nov '10 - 19:59 
Useful, visually appealing, and extremely well presented!
GeneralMy vote of 5memberMember 154261529 Oct '10 - 7:17 
Very slick. Perfect in every way. OK, I know the author, but it's still very, very good.
GeneralMy vote of 5memberYogi Yang19 Oct '10 - 1:09 
Super control. I will use it in many way that you may have not envisioned at your end!
GeneralRe: My vote of 5memberGraham Wilson19 Oct '10 - 16:31 
Thanks for your vote, and I'm truly glad that you'll be using my code. There is no higher compliment.
GeneralMy vote of 5memberR&D_Man17 Oct '10 - 22:16 
EXcellent,useful control
GeneralRe: My vote of 5memberGraham Wilson19 Oct '10 - 16:30 
Thanks for your vote! I never thought I'd get so many "5" votes on my first submission.
GeneralMy vote of 5memberWilliamCruisoring8 Oct '10 - 16:48 
Excellent Job!
GeneralRe: My vote of 5memberGraham Wilson19 Oct '10 - 16:29 
Thanks for your vote!
GeneralSandcastle is useful, but ironically poorly documentedmembermsorens21 Sep '10 - 12:59 
I applaud you for a cool control but more importantly for presenting a clear, concise description on how to use it! Many of the Code Project articles have good stuff to present but require every reader to duplicate the work of discovery in understanding and using it. I further commend you for your use and mention of Sandcastle. It is a great package, particularly when you add Sandcastle Help File Builder, but it is still challenging to use. I took all my notes on my experience with it and cobbled together an article just published last week that may interest you:
Taming Sandcastle: A .NET Programmer's Guide to Documenting Your Code.
GeneralRe: Sandcastle is useful, but ironically poorly documentedmemberGraham Wilson21 Sep '10 - 16:44 
Thanks for your comments.
 
I agree - Sandcastle is poorly documented. It took many iterations to get the help to appear just as I wanted. Nice to know it's not just me Smile | :)
 
I've read your Sandcastle article - it is Excellent! I really wish I'd read it before embarking on the task of documenting this project.
 
Coding
GeneralRe: Sandcastle is useful, but ironically poorly documentedmembertlhintoq18 Oct '10 - 7:19 
Really nice Taming Sandcastle article.
 
Any chance of that becoming a downloadable PDF? I would love to be able to put that on my iPad with my other coding books. Its a wonderful example and reference.
GeneralRe: Sandcastle is useful, but ironically poorly documentedmembermsorens18 Oct '10 - 9:15 
Absolutely! At the very top of the article there is a callout that shows the ratings and, below that, versions of the article in EPUB and PDF formats.
 
One thing that is not there (yet) though is my latest addition to accompany the article--a one-page wallchart describing everything you need to know about doc-comments. Just released last week, it is available here.
GeneralRe: Sandcastle is useful, but ironically poorly documentedmembertlhintoq18 Oct '10 - 9:21 
I'm a complete dork who didn't see that. I scanned the article. Saw it was great and filed away the bookmark for later reading when I can devote myself to it. I should have looked more closely.
 
Thank you for providing such an invaluable tool to the community.
GeneralRe: Sandcastle is useful, but ironically poorly documentedmembermsorens18 Oct '10 - 10:11 
No problem. Besides it gave me a chance to refer you to the wallchart as a bonus.
 
Thanks for the kind words!
GeneralMy vote of 5memberJaime Olivares20 Sep '10 - 15:33 
Like it. Hope to see a WPF version as well.
GeneralRe: My vote of 5memberGraham Wilson21 Sep '10 - 16:38 
Jaime,
 
Thanks for you vote. Alas, I am not a WPF programmer, so you'll not be seeing a WPF version (from me) anytime in the near future.
 
Graham
Generalawesome controlmemberChrist Kennedy13 Sep '10 - 2:59 
this is a great control!
but I only gave you a 4 because in your article you did not explain how you made it.
 
thanks for publishing this.
my code is perfect until i don't find a bug...

GeneralRe: awesome controlmemberGraham Wilson21 Sep '10 - 16:36 
Christ,
 
I'm glad you liked the control.
 
Point taken about the lack of explanation in the article.
 
Graham
GeneralMore InfomentorKunalChowdhury12 Sep '10 - 22:44 
Need more info on the control. As a reader I am not interested to run your application rather than reading it first.


Regards - Kunal Chowdhury | Software Developer | Blog | Twitter | Silverlight Tutorial | Indian Forum

Generalmy anti-virus hates you!memberChrist Kennedy12 Sep '10 - 16:41 
hi,
I ran your demoss until my anti-virus software told me not to. Its usually best to heed its advice though its done that to me with my own projects in the past.
the CircleControl looks really cool! it has a great look and seems quite flexible except my C# doesn't like it either, this is the first time I've ever seem an MS IDE step up and say "Don't run this source code it'll kill your computer" (or words to that effect). I'm sure you're not a terrorist but there seems to be something wrong. maybe its my computer, maybe it isn't... I don't know. but when I referenced the .dll file from the executables .zip file C# doesn't know its there and the intellisense can't find it.
in your article you don't explain how you wrote it. how it works. as opposed to how to 'use it'.
I wish i could use this source code but my computer's having a problem digesting it,
 
Christ
my code is perfect until i don't find a bug...

GeneralRe: my anti-virus hates you!mentorKunalChowdhury12 Sep '10 - 22:49 
It might be a VIRUS issue, might be NOT. I remember one of my C program, when I was learning the language. When I wrote a printf() and run, my AVG antivirus was blocking it saying "VIRUS". Once I removed that particular line, it was working fine. The issue was specific to that code in my PC. I run the same code in another PC of my institute. No problem was there. Once I read your message, I recalled the same instance with me from my past... Laugh | :laugh:


Regards - Kunal Chowdhury | Software Developer | Blog | Twitter | Silverlight Tutorial | Indian Forum

GeneralRe: my anti-virus hates you!memberGraham Wilson13 Sep '10 - 15:59 
Christ,
 
After reading your comments, I uploaded both .ZIP files, the .CHM file, and all of the .EXE files to www.virustotal.com, and they all came back clean (the site www.virustotal.com runs scans from all the major virus scanning vendors on uploaded files - it is a very useful tool). So I really have no idea why your anti-virus software gave a warning.
 
As for IDE warning, what version of Visual Studio are you using? I only have access to 2005. Would you be able to specify the exact wording of the warning/error message, or better yet, include a screen shot?
 
Thanks for reading the article and trying the software!
Graham
GeneralRe: my anti-virus hates you!memberChrist Kennedy14 Sep '10 - 3:21 
thank you for your response,
I'm using C#2010 at the moment and since I have run, compiled and integrated your control into my project the IDE no longer cries and neither does my anti-virus software so I cannot repeat to you what it said. My guess is i have gremlins and these gremlins do not like it when i down load a perfectly good controller like yours off the internet and use it in my own projects. gremlins are particular that way and when they don't understand the first thing about code and code-reuse they start to have issues.
thanks again for your controller and will be looking forward to more of your projects in the future,
Christ
my code is perfect until i don't find a bug...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 8 Sep 2012
Article Copyright 2010 by Graham Wilson
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid