Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#
Article

FishEye Menu in Winforms

Rate me:
Please Sign up or sign in to vote.
4.19/5 (40 votes)
12 Mar 2007CPOL5 min read 108.8K   1.9K   61   18
Brief How-To on a FishEye Menu
Screenshot - fisheye.jpg

Introduction

This is my first article, in which I try to present a basic way to make a fish eye menu control, in which the item the mouse is over is enlarged, and the surrounding items increase and decrease their size when you move the mouse.

Background

There was a post on the forums that linked to a Web site with several alternative menus that are coming in the future. I wanted to know how hard it would be to implement something like the fish eye menu, and although the code is very basic, it serves as a starting point for improving and making it better.

Using the Code

As a first version of the code, stuff is very messy. First of all, there are class and attribute names in both Spanish and English, as I wasn't thinking of posting this article. In the course of this week, I'll correct it and upload the new version.

There is a class called Item, which represents each of the different menu items. This class has very basic attributes, such as:

C#
private string _valor;
private float _inicio;
private float _fin;
private bool _over; 

_valor is the value of the item, which in this case is just a string, but it can be any supported type. _inicio and _fin are the y axis limits in which the item is displayed. _over indicates if the mouse is currently over the specified item.

The control, which in this case is the form itself, has three other attributes:

C#
private int? _indexOver;
private Item _itemOver;
private List<Item> _items;

_indexOver is the variable that holds the index of the item the mouse is over. Since it can be null, the variable is nullable. _itemOver corresponds to the specified item, and it can also be null. _items contains the item list, which in this case is loaded in the Form_Load event.

Constructor

In order to reduce the flickering and enhance the visual experience and responsiveness, there are some calls to the SetStyle method of the control in its constructor:

C#
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.CacheText, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.UserPaint, true);

What this basically does is configure the control so that I (the developer) am responsible for its drawing, and sets it to use double buffer and to cache text so it renders faster.

In the Form_Load method I just use a for to fill the _items collection, but this could be done by getting info from a datasource, receiving it as a parameter, or even setting it as a property from an external object.

The two main methods are Form1_MouseMove and OnPaint (which is overridden in this class). I will not copy the code, as it is available in the source code, but instead I will give you some insight into what it does.

OnPaint

This method is in charge of drawing the menu. It is called every time the form has to be redrawn, and it overrides the method of the base class (but it doesn't call it, so the whole painting is done in this method).

First of all, this method calculates the height of the regular items (where the mouse is not over nor near), using a basic (and not accurate) calculation of the form height over item count, just to get an average height (this calculation will be corrected in the second version of the article).

Then, what it basically does is go through the list of items in the _items collection, and check the item over property. If the property is true, then it draws the item with bigger letters and in another color. If not, it checks the index of the current item to that of the item the mouse is over at. If the index is +/- 1, or +/- 2 then it draws the item in color and gives it a bigger size. If not, it draws the item in black and with the regular size. Every time an item is drawn, the current y position (currently that variable is named x but it will be corrected) is used as the starting point of the next item. So, if the item's height is greater because the mouse is over it or it's over an item close to it, then the next item will be redrawn correctly.

Form1_MouseMove

This method controls the mouse movement. First of all, it clears the _itemOver and _indexOver class attributes. Then it goes through the _items collection and for each item it calls the Esta(float y) method. This public method of the Item class receives a y position and returns whether it is between the item's _inicio and _fin attributes (beginning and end). If it's true, then the mouse is over the item, so it sets the item's Over property to true and updates the _itemOver and _indexOver class attributes. If it's false, then it sets the Over property to false. After going through the collection, if the mouse is over an item (checking either the _itemOver or the _indexOver for nulls), it sets the mouse cursor to Cursors.Hand. Otherwise, it sets it to Cursors.Default.

Finally, it calls the this.Invalidate() method that forces a redraw, and with it, a call to the OnPaint method, which redraws the menu.

Form1_MouseClick

This last method only shows the current item, if any is selected. It could be used to call a delegate, to return a value, or anything anyone would want from a menu.

Points of Interest

What I learnt from doing this is that it's really easy to create your own controls, with your own functionality and extend the .NET Framework functionality.

What's Next?

In the next update, which I'll plan to do by the end of this week, I will:

  • Update the code to add comments
  • Rewrite the code in a better class structure
  • Change all the Spanish names for easier, English names
  • Make it a user control instead of a form
  • Read your suggestions to try and improve this menu

History

  • 03/12/2007 - First version

License

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


Written By
Web Developer
Argentina Argentina
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralNice job Pin
C#.Net3.022-Mar-07 18:10
C#.Net3.022-Mar-07 18:10 
GeneralHi there Pin
ksomi21-Mar-07 22:59
ksomi21-Mar-07 22:59 
GeneralNice! Pin
Muammar©19-Mar-07 22:10
Muammar©19-Mar-07 22:10 
GeneralRe: Nice! Pin
Muammar©22-Mar-07 6:45
Muammar©22-Mar-07 6:45 
GeneralRe: Nice! Pin
C#.Net3.022-Mar-07 18:09
C#.Net3.022-Mar-07 18:09 
GeneralRe: Nice! Pin
Muammar©23-Mar-07 11:11
Muammar©23-Mar-07 11:11 
GeneralGreat Job And A Bug Pin
Bassam Abdul-Baki12-Mar-07 9:36
professionalBassam Abdul-Baki12-Mar-07 9:36 
GeneralRe: Great Job And A Bug Pin
Matias Szulman12-Mar-07 9:40
Matias Szulman12-Mar-07 9:40 
GeneralInteresting Concept Pin
NormDroid12-Mar-07 9:22
professionalNormDroid12-Mar-07 9:22 
GeneralGood Article! Pin
Dave Kreskowiak12-Mar-07 8:15
mveDave Kreskowiak12-Mar-07 8:15 
GeneralPointer Selection... Pin
code-frog12-Mar-07 7:38
professionalcode-frog12-Mar-07 7:38 
GeneralGood job! Pin
Member 9612-Mar-07 7:23
Member 9612-Mar-07 7:23 
GeneralNot bad Pin
Marc Clifton12-Mar-07 6:16
mvaMarc Clifton12-Mar-07 6:16 
GeneralRe: Not bad Pin
Matias Szulman12-Mar-07 7:29
Matias Szulman12-Mar-07 7:29 
GeneralRe: Not bad Pin
David Stone12-Mar-07 8:30
sitebuilderDavid Stone12-Mar-07 8:30 
GeneralRe: Not bad Pin
Marc Clifton12-Mar-07 8:58
mvaMarc Clifton12-Mar-07 8:58 
GeneralRe: Not bad Pin
daniilzol12-Mar-07 7:55
daniilzol12-Mar-07 7:55 
GeneralRe: Not bad Pin
Matias Szulman12-Mar-07 9:00
Matias Szulman12-Mar-07 9:00 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.