ArticleDirectory
- Constructor
- Testing and using the control
This article from http://www.codeproject.com/KB/miscctrl/cs_star_rating_control.aspx
A c # star rating control.
- Download Demo project-13.3 KB
- Download source-5.03 KB
Introduction
This is a control similar to one used on the Netflix movie website, and one used by the iTunes and Microsoft Media Player to rate the songs in the song library.
Background
There is an implementation of such a control in C ++, which can be found here: starcontrol.
This article and code were influenced by a recently published msdn magazine article by Duncan macenzie, which is available here: creating a five-star rating control. His implementation is in Visual Basic. net.
This control is implemented in C #.
The Code
- Create a class library project.
- Inherit from
System. Windows. Forms. Control
.
- Add a project reference toSystem. Windows. Forms. dll.
This is not done automatically, and can be done by Right clicking on "references" in the Solution Explorer and choosing the wanted DLL.
Constructor Collapse | Copy code
PublicStarratingcontrol () {setstyle (controlstyles. allpaintinginwmpaint,True); Setstyle (controlstyles. userpaint,True); Setstyle (controlstyles. doublebuffer,True); Setstyle (controlstyles. resizeredraw,True); Width =120; Height =18; M_starareas =NewRectangle [starcount];}
Collapse | Copy code
>>> Setstyle (controlstyles. allpaintinginwmpaint,True);
This lowers the flickering during background painting by telling the control to ignoreWm_erasebkgnd
Message. This only works however whenControlstyles. userpaint
Control style is set.
Collapse | Copy code
>>> Setstyle (controlstyle. userpaint,True);
This indicates that the control is going to paint itself.
Collapse | Copy code
>>> Setstyle (controlstyles. doublebuffer,True);
This indicates that the control will use double buffering to draw itself. This will also reduce flicker.Controlstyle. userpaint
AndControlstyle. allpaintinwmpaint
Shocould be set in conjunction with this bit.
Collapse | Copy code
>>> Setstyle (controlstyles. resizeredraw,True);
This indicates that the control is to redraw itself upon being resized.
Leftmargin
,Rightmargin
,Topmargin
, AndBottommargin
Are properties exposing the following private data members:
Collapse | Copy code
Private IntM_leftmargin =2;Private IntM_rightmargin =2;Private IntM_topmargin =2;Private IntM_bottommargin =2;
The public properties are mostly of the form:
Collapse | Copy code
Public IntLeftmargin {Get{ReturnM_leftmargin ;}Set{If(M_leftmargin! = Value) {m_leftmargin = value; invalidate ();}}}
Please notice that setting the property forces a redraw of the control if the value of the margin changes. this logic also holds for some of the other properties dealing with modifiable Visual Properties of the control, such as the property exposing the number of stars to draw.
Collapse | Copy code
Protected Override Void Onpaint (Painteventargs PE) {PE. Graphics. Clear (backcolor ); Int Starwidth = (width-(leftmargin + rightmargin + (starspacing * (starcount- 1 )/Starcount; Int Starheight = (height-(topmargin + bottommargin); rectangle drawarea = New Rectangle (leftmargin, topmargin, starwidth, starheight ); For (Int I = 0 ; I <starcount; ++ I) {m_starareas [I]. x = drawarea. X-starspacing/ 2 ; M_starareas [I]. Y = drawarea. Y; m_starareas [I]. width = drawarea. Width + starspacing/ 2 ; M_starareas [I]. Height = drawarea. height; drawstar (PE. Graphics, drawarea, I); drawarea. x + = drawarea. Width + starspacing ;} Base . Onpaint (PE );}
All of the drawing is done in this method. For the implementation of this method, we will need to add a referenceSystem. Drawing. dll.
Collapse | Copy code
>>> PE. Graphics. Clear (This. Backcolor)
This erases the background.
Collapse | Copy code
IntStarwidth = (width-(leftmargin + rightmargin + (m_starspacing * (m_starcount-1)/m_starcount;IntStarheight = (height-(topmargin + bottommargin ));
This calculates the width and height of the stars given the control width and height and the inner margins.
Then we draw each of the stars.
Collapse | Copy code
Protected Void Drawstar (Graphics g, rectangle rect, Int Starareaindex) {brush fillbrush; pen outlinepen = New Pen (outlinecolor, outlinethickness ); If (M_hovering & m_hoverstar> starareaindex) {fillbrush = New Lineargradientbrush (rect, hovercolor, backcolor, lineargradientmode. forwarddiagonal );} Else If ((! M_hovering) & m_selectedstar> starareaindex) {fillbrush = New Lineargradientbrush (rect, selectedcolor, backcolor, lineargradientmode. forwarddiagonal );} Else {Fillbrush = New Solidbrush (backcolor);} pointf [] P = New Pointf [ 10 ]; P [ 0 ]. X = rect. x + (rect. width/ 2 ); P [ 0 ]. Y = rect. Y; P [ 1 ]. X = rect. x + ( 42 * Rect. width/ 64 ); P [ 1 ]. Y = rect. Y + ( 19 * Rect. Height/ 64 ); P [ 2 ]. X = rect. x + rect. width; P [ 2 ]. Y = rect. Y + ( 22 * Rect. Height/ 64 ); P [ 3 ]. X = rect. x + ( 48 * Rect. width/ 64 ); P [3 ]. Y = rect. Y + ( 38 * Rect. Height/ 64 ); P [ 4 ]. X = rect. x + ( 52 * Rect. width/ 64 ); P [ 4 ]. Y = rect. Y + rect. height; P [ 5 ]. X = rect. x + (rect. width/ 2 ); P [ 5 ]. Y = rect. Y + ( 52 * Rect. Height/ 64 ); P [ 6 ]. X = rect. x + ( 12 * Rect. width/ 64 ); P [ 6 ]. Y = rect. Y + rect. height; P [ 7 ]. X = rect. x + rect. width/ 4 ; P [ 7 ]. Y = rect. Y + ( 38 * Rect. Height/ 64 ); P [ 8 ]. X = rect. X; P [ 8 ]. Y = rect. Y + ( 22 * Rect. Height/ 64 ); P [9 ]. X = rect. x + ( 22 * Rect. width/ 64 ); P [ 9 ]. Y = rect. Y + ( 19 * Rect. Height/ 64 ); G. fillpolygon (fillbrush, P); G. drawpolygon (outlinepen, P );}
The areas in which the stars are to be drawn are contained in an array System: Drawing: rectangle
Objects. This array is of the same size as the number of stars and is resized whenever the Count of stars changes through Starcount
Property. each of the stars is filled with a gradient brush. this is an arbitrary demo-, but one which I hope wowould yield more pleasing results than a solid color wowould. the 10 points of each star are then calculated. I found it easiest to implement the star by dividing the drawing area into 64 logical partitions and placing the horizontal and vertical points as the offset into the 64 segment space. finally, we fill the polygon specified by these ten points with the forward-diagonal, gradient brush, and outline the polygon with the outline pen. lastly, we implement the event handlers dealing with varous mouse functions. when the mouse enters the control, we set the hovering flag True
And force a repaint of the control:
Collapse | Copy code
Protected Override VoidOnmouseenter (system. eventargs EA) {m_hovering =True; Invalidate ();Base. Onmouseenter (EA );}
When the mouse leaves the control, we clear that flag and once again force a repaint of the control:
Collapse | Copy code
Protected Override VoidOnmouseleave (system. eventargs EA) {m_hovering =False; Invalidate ();Base. Onmouseleave (EA );}
Whenever the mouse moves, we check each of the stars in order to see over which, if any, the mouse is hovering. We record this information and force a repaint of the control.
Collapse | Copy code
Protected Override VoidOnmousemove (mouseeventargs ARGs ){For(IntI =0; I <starcount; ++ I ){If(M_starareas [I]. Contains (ARGs. X, argS. y) {m_hoverstar = I +1; Invalidate ();Break;}}Base. Onmousemove (ARGs );}
The logic is similar whenever a user clicks on the control. we iterate over each of the stars to see which, if any, was clicked on, and record this information. we then force a repaint of the control.
Invalidate ()
Eventually turns into a callOnpaint
Which CILSDrawstar ()
.Drawstar ()
, In turn, will check our recorded hover and click information to figure out how to render the control.
Testing and using the control
Any project using the control must first Add a reference just like we added a reference toSystem. Drawing. dll. Add"Usingratingcontrols
"ToUsing
Declarations.
The sample application does little else than depend on the default properties provided by the control, which include 5 stars with a dark gray outline, a yellow hover color, and a royal blue selected color. the control also has a default width and height of 120 and 18 pixels respectively. I found that this ratio produced the most pleasing looking image. the height and width are, of course, modifiable, and your stars can thus look as tall or as wide as you please.
Really, the only mildly-interesting piece of code in the sample application is in the constructor. The following is the piece of code, in its entirety:
Collapse | Copy code
PublicMainform () {initializecomponent (); m_starratingcontrol.top =45; M_starratingcontrol.left =85; Controls. Add (m_starratingcontrol );}
Possible improvements
The control cocould benefit from more publicly exposed properties such as fill brush style, so that forward-diagonal gradient is not the only implementation.
If speed optimization is a priority over size, one can rewriteOnpaint()
Method so that it uses cached pre-calculated points to draw each of the stars instead of calculating the points on the fly. this wowould necessitate a few more data members to store the cached data, but wowould greatly improve the rendering speed of the control. this will probably be one of my own improvements of this control.
If you are really ambitious, you can follow the example of the msdn magazine article and implement functionality to render a user-supplied image rather than a star. this wowould, of course, necessitate a name change, as it wowould no longer technically be a * star * rating control.
PleaseSend meAny comments/questions/suggestions. I will strive to implement any valid suggestions for improvements and answer all of your questions.
License
This article has no explicit license attached to it but may contain in usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here
About the author