![]() |
Desktop Development »
Menus »
Menus and Toolbars
Intermediate
Circular Menu ComponentBy Mark J BiddlecomDescribes the use and architecture of an animated, circularly arranged pop-up menu control written in C#. |
C#, VB, Windows, .NET 1.1, GDI+, WinForms, VS.NET2003, Architect, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

The circular menu component is a .NETTM user interface component that I developed as part of a usability class project. It is an animated menu popup that displays option icons in a circle around the position where the popup was launched.
Although I designed and wrote the code for the tool, my team members made several contributions to the philosophy behind the layout:
Many thanks for their valuable input and suggestions.
Circular menus have a few advantages when compared to traditional popups:
There are, of course, disadvantages to a circular menu, a few of which are described here:
This document provides a generic overview of the structure of the circular menu component, as well as a tutorial on its use in third-party applications.
THIS SOFTWARE AND THE ACCOMPANYING DOCUMENTATION ARE PROVIDED �AS IS�, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE TO OR LOSS OF DATA THAT THIS PRODUCT MAY CAUSE.
When modified, this product (source code, binaries, and documentation) may be freely redistributed for both commercial and personal use. Acceptable modifications are defined to be: additions, meaningful changes to the source code, or inclusion of the source code or binaries within a larger software package.
I request, but do not require, credit for my work. This can be placed in your product�s documentation or directly in the software via a typical �Help/About� screen.
Before you can create programs using the circular menu component, you need to add the component to your Visual Studio toolbox panel.
To do this, right-click anywhere on the toolbox and select �Add/Remove Items��.
Selecting this menu option will cause the �Customize Toolbox� dialog to be displayed. From here, you must first click the �Browse�� button, and then navigate to the directory where you installed the circular menu.
Once you have found the installation directory, select the �CircularMenu.dll� file and then select �Open.� This will return you to the �Customize Toolbox� dialog. Click �OK� and you will be returned to Visual Studio. New icons for the circular menu component and the menu option component will have been added to your toolbox.
Create a new Windows application project in the language of your choice. Once the template is ready, you will have a new form in your project named Form1. Ensure that the circular menu has been added to your toolbox as described earlier. Once you have done so, you can drag and drop it from the toolbox onto your form. Do this now. Notice that Visual Studio has created an icon in your component tray named �circularMenuPopup1�. You can modify the properties of the circular menu via the Properties window, just as with any other control.
These properties work together to control the appearance and behavior of your circular menu. The OpeningAnimation and ClosingAnimation properties define the animations played when the menu opens and closes, respectively. The ToolTip property controls how and where the menu�s tool tips are displayed.
We�ll accept the default values for now. To add items to the menu, we will need to work with the MenuOptions property. To do this, select MenuOptions in the property editor and click on the ��� button that appears to the right.
This summons a �MenuOption Collection Editor� dialog that can be used to add options to the menu and configure them. Click the �Add� button three times to add three new options to the menu.
Notice that you can edit the properties for your menu directly within this dialog. Each option is added to the Visual Studio designer component tray, so they can also be edited outside of this dialog.
Select the first option, �menuOption1�. We will make this an �Open� button. Follow the steps below to do this:
HoverImage� property and click on the ��� button to the right. This summons the �Edit Menu Option Image� dialog, which you can use to control the appearance of the option. Select �Browse�� and then navigate to your Visual Studio program directory (typically, �C:\Program Files\Microsoft Visual Studio� or �C:\Program Files\Microsoft Visual Studio .NET 2003�). From here, choose the �Common7� directory and then open �Graphics�. This directory contains a number of generic icons and bitmaps that you can use throughout your applications.
ffc0c0c0� to make the background for this image transparent, and then click �OK�.
DisabledImage, Image, and PressedImage properties.
ToolTip property to �Open a file.� Repeat these steps to make menuOption2 a �Save� button and menuOption3 a �Print� button. When you�re done, click the �OK� button on the �MenuOption Collection Editor� dialog.
Because the icons we�ve used are rather small, we�ll need to decrease the radius for our popup menu. Select �circularMenuPopup1� from the component tray and then change the Radius property from 50 to 25.
Let�s also change the animation a bit: select the �OpeningAnimation� property and click on the ��� button to the right. In the edit dialog that appears, change the layout style to �Spin along perimeter�, change the effect style to �Zoom in�, and then change the number of frames to �30�. Frames are displayed at approximately 30 per second, so this makes our opening animation nearly one second long.
Click the Play button to preview the animation, and then click �OK� to close the dialog. We�re finally ready to enable the popup. When and why a popup displays is up to you, but a very common trigger is right-clicking on the form. To enable this, perform the following steps:
MouseUp� event and double-click it. This will launch the code editor with a newly added method called �Form1_MouseUp�.
if( e.Button == MouseButtons.Right )
circularMenuPopup1.Popup( this, e.X, e.Y );Form1 class: Private Sub Form1_Click(ByVal sender as Object, ByVal e As MouseEventArgs)_
Handles MyBase.MouseUp
If e.Button = MouseButtons.Right Then
CircularMenuPopup1.Popup(Me, e.X, e.Y)
End If
End SubWe�re now ready to test the application! Run the application using the �Debug > Start� menu option (F5 shortcut key for Visual Basic users), and right-click anywhere on your form to summon the shortcut menu!
When the circular menu is shown, it obtains the system�s input focus, and does not release it until the user either cancels the menu or selects an option. These user actions are communicated to your program via a set of events, the most important of which are the MenuOption class� �Click� event and the CircularMenuPopup class� �OptionSelected� event.
The primary distinction between these events is that the �Click� event is fired by a specific menu option, whereas the circular menu itself fires the �OptionSelected� event. This means that when writing methods to handle �Click�, only code specific to a single menu option needs to go in each method. On the other hand, writing an �OptionSelected� handler implies including code for each menu option within that menu handler.
These two events are provided primarily to support different styles, but OptionSelected does offer slightly more information than Click�specifically, the coordinates where the menu was displayed. If you need to know where the menu was when a user selected an option, you need to use the OptionSelected event. Please review the code documentation for more details.
In this tutorial, we�ll write some code to handle the Click event of our Open button. To do so, close any open dialogs and select menuOption1 from the component tray. Double-clicking on menuOption1 will automatically add a new method to your Form1 class that can handle the Click event for that option. By default, it�s called �menuOption1_Click.�
For demonstration purposes, we�ll show an open dialog from within this method. The code for this is shown below:
private void menuOption1_Click(object sender, System.EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.ShowDialog( this );
}
Private Sub MenuOption1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MenuOption1.Click
Dim dialog As New OpenFileDialog
dialog.ShowDialog(Me)
End Sub
Now, when you run your program and click on the Open file button in the menu, the system will show an Open file dialog.
Other events offered by the system allow you to be notified when the user moves the mouse cursor over an option (MenuOption.StartHover) or away from an option (MenuOption.EndHover). You can also handle CircularMenuPopup.MenuCanceled to be notified when a user cancels a menu without selecting anything, and CircularMenuPopup.MenuClosed to be notified whenever a menu is closed, regardless of the option selected.
Animations within the library are handled by classes that implement IFrameLayoutManager and IFrameModifier. These classes are responsible for arranging the options around the clicked position and applying animation effects. Review the documentation and default implementations for more details on writing your own animations and effects. These classes are applied to the FrameImageEffect and LayoutAnimator properties of the MenuAnimation class. Both a menu�s opening and closing animations are instances of the MenuAnimation class.
Similarly, menu tool-tips are drawn by a class that implements the IToolTipRenderer interface. You can specify a custom tool-tip renderer via the CircularMenuPopup.ToolTipOverride property.
The code for the circular menu component has been written in the C# language, available from Microsoft. While the .NET SDK and C# compiler are freely available, the circular menu code makes extensive use of the additional features supplied by the Visual Studio development environment, and so this documentation is written from the perspective of a developer using Visual Studio.
As part of the second major release of the .NET platform (2.0), Microsoft plans on providing free versions of Visual Studio targeted at students and hobbyists interested in C# and Visual Basic. Early documentation on these environments indicates that they will have similar functionality to their retail counterpart, but will limit the complexity of the applications written using them.
There is also a freeware designer that can be used to gain access to the Visual Studio features of the circular menu assembly. As of the writing of this article, information on this designer can be found at the following URLs:
The code for the circular menu component has been separated into two separate assemblies, CircularMenu.dll and PixelEffects.dll. The PixelEffects library was written to provide generic special effects for drawing within the CircularMenu library, and will not be discussed within this document. The source code for it, however, is included.
The following diagram shows the basic classes that are a part of the Circular Menu library:

The Circular Menu library can be thought of as being comprised of three subsystems: Top-Level Objects, Menu Options and Rendering, and Menu Animations. The top-level objects serve as controllers for the objects in the other two subsystems.
In this architecture, the CircularMenuPopup class represents the top-most entity. In dependency chains beginning here, you will be able to access most of the functionality provided by this library.
The other top-level component, CircularMenuWindow, is a System.Windows.Forms.Form subtype that is capable of animating and displaying a circular menu. This is the class responsible for delivering a user�s selection to the requesting application. While the Menu Window is a publicly available and usable class, there are few situations where you would use it directly instead of via a Menu Popup object.
A circular menu popup is composed of four objects: a collection of menu options, an opening animation, a closing animation, and a tool tip renderer. These objects, in turn, are composed of several elements.
The MenuOptionCollection class is a simplistic implementation of the .NET CollectionBase class, strongly typed for non-null MenuOption objects. This class exposes a method that returns a sub-collection of all menu options that are currently visible. This sub-collection is used when a window is being shown or animated.
Instances of the MenuOption class describe an individual element in the popup menu. These are analogous to the MenuItem instances in traditional .NET menus. Each option maintains an enabled state, a visible state, and a tool tip, as well as a number of images that control the visual appearance of the option when rendered. These images, which are stored in the form of MenuOptionImage instances, are Image, DisabledImage, HoverImage, and PressedImage. These will be described in detail later.
Each MenuOptionImage object defines an icon and optional operations (color key, transparency, and drop shadow) on the image. When the system needs to draw the image associated with the object, this class performs each of the optional operations on that image one at a time and returns the rendered result. Because this process can be expensive, each MenuOptionImage stores a cached version of the result. The cached result is cleared (and rendered again when requested) whenever any of the options for that image change.
Finally, the DropShadowOptions class encapsulates options that define the behavior of the drop shadow algorithm that is applied to an image when requested. Each instance of MenuOptionImage has a corresponding instance of DropShadowOptions.
Finally, the IToolTipRenderer interface describes an object that is capable of drawing the tool tips for the popup menu. CircularMenu.dll provides a default implementation of this interface in StandardToolTipRenderer. This renderer simply places the tool tip within the center of the pop-up menu. It exposes several options that are configurable via the Visual Studio designer.
An extended version of the tool tip render is described by the IExtraSpaceToolTipRenderer interface and its default implementation, BelowMenuToolTipRenderer. This type of tool tip renderer might require extra space on a menu surface that isn�t accounted for by simply the location and sizes of the menu option. For example, the below-menu renderer places the tool tip below all menu options and therefore increases the overall height of the menu. These types of tool-tip renderers need to report their additional requirements to the system so that the menu can be created and positioned properly.
Both StandardToolTipRenderer and BelowMenuToolTipRenderer inherit the base class StandardToolTipData, which provides a common list of designer-aware options, such as foreground color and border thickness.
The MenuAnimation class represents one of the two menu animations defined for each popup menu. This class defines the number of frames to animate, as well as the layout and modifications for those frames. Because generating animations can be expensive, this class also maintains a cached copy of a built animation, in the form of a FrameCollection instance. This copy will need to be cleared when setting changes would produce a different animation.
The IFrameLayoutManager interface describes objects that are capable of defining the layout for each frame in a menu animation. There are a number of built-in implementations of this interface, and customized implementations can be used as well.
Similarly, objects implementing the IFrameModifier interface are used to provide �special effects� in the menu animations. Unlike layout managers, frame modifiers are not applied to the final frame in an animation. Because of this, they should produce an effect that gradually moves towards the normal image state. As with layout managers, there are a number of default implementations built into the CircularMenu.dll assembly.
Menu animations come in two flavors: forward and reverse. Each type uses a layout manager and a frame modifier, but they render their frames in different orders.
ForwardMenuAnimation renders frames starting from index zero and advancing towards the highest index, while ReverseMenuAnimation begins rendering at the highest frame index and finishes at zero. These instances produce a ForwardFrameCollection and ReverseFrameCollection, respectively. Typically, a forward animation will be used for the popup opening and a reverse animation will be used for popup closing. To maintain flexibility of the library, this convention is not enforced.
Each FrameCollection instance is an immutable, fully rendered animation. The cached animations stored in MenuAnimation instances are objects of one of the subtypes of this class. When creating a frame collection, the new instance is supplied with a set of menu options to render, a menu radius, a layout manager, and a frame modifier. The object�s constructor will then proceed to render the frames, applying the layout and modifications it was supplied with. Note that the FrameCollection class was not designed to be the superclass for objects outside of the CircularMenu.dll assembly.
The source files included with this article provide a comprehensive documentation set rendered from the C# XML comments, using nDoc. These describe the architecture and options in much greater detail than this document. The provided installer project will add the documentation set to Visual Studio so that the help is available when authoring applications utilizing the circular menu.
Rewrote animation thread to use Control.Invoke instead of directly calling methods on a control from a separate thread. Special thanks to the members who have commented on the article, and especially DrewS, who pointed out the solution. This was a problem that never manifested on any of the machines I was using--please let me know if it didn't work, after all.
Also, added files necessary to build the installer. This made the ZIP file a bit larger, hope nobody minds :)
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 21 Jan 2005 Editor: Smitha Vijayan |
Copyright 2005 by Mark J Biddlecom Everything else Copyright © CodeProject, 1999-2009 Web21 | Advertise on the Code Project |