Click here to Skip to main content
16,020,459 members
Articles / Web Development / ASP.NET

Silverlight Prize Wheel Animation Using Custom Circular ListBox Control

Rate me:
Please Sign up or sign in to vote.
4.44/5 (5 votes)
25 May 2010CPOL5 min read 75.8K   1.9K   16   19
This article describes a Silverlight wheel animation using a custom circular ListBox control.

Introduction

This article describes a Silverlight custom control listbox with a RadialPanel as a prize wheel. The wheel can be loaded with text, number, or image data. Using the spin button, we can start the wheel animation. The wheel would spin and select a random item. A second animation displays the selected winning item. This project shows how to design custom controls and animation in Silverlight using code-behind and XAML.

Background

Silverlight custom controls are very flexible. Using the templates, we can design custom controls which look different than customary rectangular ones. This project uses a custom circular list box and a RadialPanel for the prize wheel. The design is inspired by excellent blogs on building custom controls by Jeff Prosise, Scott Guthrie, and Jit Ghosh. These are listed in the references at the end of this article.

Output

The page looks like the image shown below. A live demo is available at this site: Prize Wheel Live Demo.

Prizewheel.jpg

When the page gets loaded, the prize wheel (with a default diameter of 300) is loaded with text data and displayed. The diameter of the wheel can be changed by using the numeric up down counter. The wheel will be modified with the new diameter when the Radius Load button is pressed. The data contents of the wheel can be changed by picking one of three options: number, text, or images, and pressing the Data Load button.

Picture wheel is shown below:

Prizewheel_images.jpg

The number wheel is shown below:

Prizewheel_Numbers.jpg

When the spin button is pressed, a random number box is chosen and the wheel rotation starts. The counter on the top right corner gets loaded with the number of steps for the wheel animation. This will count down to zero when the wheel is rotating. The wheel rotates up to the randomly chosen item and stops. Then the second animation starts. The chosen item is magnified, translated, and flipped (using plane projection animation), and displayed at the center of the wheel. The chosen item number and the angle are displayed in a list box on the right top corner. This list box and counter are for debugging purposes, and can be removed (or collapsed) if needed.

The winner selected image is shown below:

Prizewheel_Winner.jpg

Code Description

Prize Wheel Control Template

One of the most powerful features of Silverlight is the ability to completely customize the look and feel of the controls. I have used these capabilities in designing the prize wheel as a custom control, which is the main element of this project. It is a list box with a circular shape and a RadialPanel. Please refer to excellent articles on custom controls by Scott Guthrie (reference 2) and Jit Ghose (reference 3) given below. The shape of the list box is determined by the template which is coded in the App.Xaml file resources. Here we are declaring an Ellipse as the control shape, with a gradient fill. By this trick, we are replacing the control's visual tree with an ellipse, the dimensions of which can be defined at run time. By making the ellipse width and height equal, we can create a circle as the shape of the control.

C#
<Style TargetType="ListBox" x:Key="RoundListBox_Style" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Grid>
<Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5" 
Center="0.5,0.5" >
<GradientStop Color="SteelBlue" Offset="0.1" />
<GradientStop Color="RoyalBlue" Offset="0.25" />
<GradientStop Color="LightSkyBlue" Offset="0.50" />
<GradientStop Color="LightBlue" Offset="0.75" />
<GradientStop Color="BurlyWood" Offset="1.0" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ItemsPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

ItemsPresenter: The next interesting thing in the template is the ItemsPresenter. This lets us preserve the multiple item content model, and we will be doing another trick to use the RadialPanel as the items presenter.

Custom RadialPanel

The RadialPanel uses the excellent work done by Jeff Prosise (reference 1), which is in RadialPanel.Cs file. In this class, MeasureOverride and ArrangeOverride do the heavy lifting to arrange the listbox items in a radial layout. The prize wheel code in XAML is given below:

XML
<ListBox
   Style="{StaticResource RoundListBox_Style}"
   x:Name="Prize_ListWheel">
<ListBox.RenderTransform>
<RotateTransform x:Name="Prize_ListWheel_Rotate"
   Angle="0" >
</RotateTransform>
</ListBox.RenderTransform>
<ListBox.ItemsPanel>
<ItemsPanelTemplate x:Name="RPIPT1">
<custom:RadialPanel x:Name="RadialPanel1" 
   Loaded="RadialPanel1_Loaded"
   ItemAlignment="Center"
   ItemOrientation="Rotated" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

The RadialPanel1_Loaded event handler gets the RadialPanel radius, and uses it to modify the wheel parameters by calling ModifyWheelParameters().

C#
private void RadialPanel1_Loaded(object sender, RoutedEventArgs e)
{
    radialpanelxaml =(RadialPanel) sender;
    ModifyWheelParameters();
    Load_Wheel_Data();
}

ModifyWheelParameters calculates the radius value from the up down counter, and adjusts the Prize_ListWheel ellipse width and height using a variable, wheelradiusfactor, which is 2.4. This factor fits the RadialPanel inside the listbox circular shape.

C#
private void ModifyWheelParameters()
{
    Prize_ListWheel.ItemsSource = "";
    radialpanelradius = Convert.ToInt16(Counter_Radius.Value);
    radialpanelxaml.Radius = radialpanelradius;
    Prize_ListWheel.Height = radialpanelradius * wheelradiusfactor;
    Prize_ListWheel.Width = radialpanelradius * wheelradiusfactor;
    Prize_ListWheel_Rotate.CenterX = Prize_ListWheel.Width/2;
    Prize_ListWheel_Rotate.CenterY = Prize_ListWheel.Height/2;
    Canvas.SetLeft(Prize_ListWheel, ListWheelLeft);
    Canvas.SetTop(Prize_ListWheel, ListWheelTop);
    RectMaskCreate();
}

Animation Code

There are two animations in this project. The main animation is the prize wheel rotation. It is very simple and straightforward. This code in the Main.Xaml determines the rotation transform:

XML
<ListBox.RenderTransform>
<RotateTransform x:Name="Prize_ListWheel_Rotate"
      Angle="0" >
</RotateTransform>
</ListBox.RenderTransform>

When the Spin button is pushed, the SpinCode_Start_Click event is raised, which picks a random number and calculates the angle to be rotated. The variable numberofrotations is set to be two rotations (so that the wheel will rotate twice) + the random number listbox item angle. The variable anglefactor is set to be 4 to increase the number of steps of rotation to produce a smooth rotating wheel motion. This event also starts the myDispatchTimer, which is a 25 ms timer set in MainPage().

C#
private void SpinCode_Start_Click(object sender, RoutedEventArgs e)
{
    //Bring box to original position from previous win
    Normal_Shift_Box(oldrandomnumber);
    Random random = new Random();
    if (Pictures_Button.IsChecked==false)
    {
    randomnumber = random.Next(0, itextmaxcount);
    animanglerot =360.0 / Convert.ToDouble(itextmaxcount);
    anglesteps = Convert.ToInt64((itextmaxcount*numberofrotations
    - randomnumber+oldrandomnumber) * anglefactor);
    }
    else
    {
    randomnumber = random.Next(0, iimagemaxcount);
    animanglerot = 360.0 / Convert.ToDouble(iimagemaxcount);
    anglesteps = Convert.ToInt64((iimagemaxcount *
    numberofrotations - randomnumber + oldrandomnumber) * anglefactor);
}
    anglesteps--;
    Prize_ListWheel_Rotate.Angle += animanglerot / anglefactor;
    myDispatcherTimer.Start();
}

Every 25 ms, the Each_Tick event is raised. This event rotates the wheel one step till the anglesteps counter goes to zero.

C#
public void Each_Tick(object o, EventArgs sender)
{
    if (anglesteps > 0)
    {
        Prize_ListWheel_Rotate.Angle += animanglerot / anglefactor;
        anglesteps--;
    }
    else
    {
        ListBoxWinners.Items.Add(randomnumber.ToString()+", "    + 
                                 Prize_ListWheel_Rotate.Angle.ToString());
        myDispatcherTimer.Stop();
        Winner_Animation();
        oldrandomnumber = randomnumber;
    }
    TextBox_Counter.Text = anglesteps.ToString();
}

When anglesteps goes to zero, the second animation and the myDispatcherTimer2 is started.

This animation uses Scale_Shift_Box which uses PlaneProjection, ScaleTransform, and TranslateTransform to create the second animation to bring the winning item to the top center of the prize wheel.

C#
private void Winner_Animation()
{
    double Scale=1;
    double translatefactor=-0.5;
    int selectedbox = randomnumber;
    TextWinner.Visibility = Visibility.Visible;
    transform_animation_method(Scale, translatefactor, selectedbox);
}

private void Scale_Shift_Box(int boxnumber)
{
    TransformGroup tg = new TransformGroup();
    ScaleTransform st = new ScaleTransform();
    PlaneProjection pp = new PlaneProjection();
    st.ScaleX = 1 + Scale10 / step10;
    st.ScaleY = 1 + Scale10 / step10;
    TranslateTransform tt = new TranslateTransform();
    tt.X = xincrement * (stepmax - step10);
    tt.Y = yincrement * (stepmax - step10);
    tg.Children.Add(st);
    tg.Children.Add(tt);
    if (Pictures_Button.IsChecked == true)
    {
        imagelist[boxnumber].RenderTransform = tg;
        imagelist[boxnumber].Projection = pp;
        pp.RotationY = 15 * step10;
    }
    else
    {
        namelist[boxnumber].RenderTransform = tg;
        namelist[boxnumber].Projection = pp;
        pp.RotationY = 15 * step10;
    }
}

Points of Interest

Custom controls are one of the very interesting parts of Sivlerlight. The articles by Jeff Prosise, Scott Guthrie, Jit Ghosh will give you a very good overview of custom controls. You can also use Xamlpadx-v2 from Lester's blog (reference 4) to understand the Visual Tree in Silverlight controls using the information provided in Jit Ghosh's blog.

References

  1. Radial Layout in Silverlight, blog by Jeff Prosise on the RadialPanel control.
  2. Silverlight Tutorial Part 7: Using Control Templates to Customize a Control's Look and Feel, blog by Scott Guthrie about custom controls.
  3. WPF Control Templates - An Overview, Jit Ghosh's blog on custom controls
  4. Xamlpadx-v2, link to Xamlpadx-v2 from Lester's XML blog, an excellent tool for understanding custom controls.
  5. Prize Wheel Live Demo: A live demo of this project.

Other Articles by the Author

History

This is the first version of this article.

License

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


Written By
Architect
United States United States
Vijay Kumar: Architect, Programmer with expertise and interest in Azure, .net, Silverlight, C#, WCF, MVC, databases and mobile development. Concentrating on Windows Phone 7 and Windows Azure development. Lived in California for many years and done many exciting projects in dotnet and Windows platforms. Moved to Raleigh (RTP), North Carolina recently and available for consulting.  Blog http://Silverazure.blogspot.com.

Comments and Discussions

 
BugError after opening source code Pin
Rujj26-May-14 23:09
Rujj26-May-14 23:09 
QuestionAdding Sound Pin
Member 802387321-Jun-11 4:12
Member 802387321-Jun-11 4:12 
AnswerRe: Adding Sound Pin
silverazure22-Jun-11 6:17
silverazure22-Jun-11 6:17 
GeneralRe: Adding Sound Pin
Member 802387323-Jun-11 3:42
Member 802387323-Jun-11 3:42 
RantSilverazure I have a question for you Pin
Member 801263817-Jun-11 4:07
Member 801263817-Jun-11 4:07 
GeneralRe: Silverazure I have a question for you Pin
silverazure17-Jun-11 16:25
silverazure17-Jun-11 16:25 
GeneralRe: Silverazure I have a question for you Pin
silverazure17-Jun-11 19:48
silverazure17-Jun-11 19:48 
QuestionDumb End-user Pin
John Prindle15-Aug-10 12:37
John Prindle15-Aug-10 12:37 
AnswerRe: Dumb End-user Pin
silverazure5-Sep-10 21:49
silverazure5-Sep-10 21:49 
GeneralGood Article Pin
Jitendra Zaa13-Jun-10 23:59
Jitendra Zaa13-Jun-10 23:59 
GeneralNice Pin
AspDotNetDev3-Jun-10 20:00
protectorAspDotNetDev3-Jun-10 20:00 
GeneralRe: Nice Pin
silverazure3-Jun-10 20:50
silverazure3-Jun-10 20:50 
I am happy you liked it.
I would have loved to embed the sivlerlight demo in the article, but do not know how to do it. That is why I provided an external link to a live demo.
GeneralInteresting Stuff [modified] Pin
Alan Beasley25-May-10 7:20
Alan Beasley25-May-10 7:20 
GeneralRe: Interesting Stuff Pin
silverazure25-May-10 18:45
silverazure25-May-10 18:45 
GeneralRe: Interesting Stuff Pin
Alan Beasley25-May-10 22:46
Alan Beasley25-May-10 22:46 
GeneralRe: Interesting Stuff Pin
Jim McCurdy1-Jun-10 4:10
Jim McCurdy1-Jun-10 4:10 
GeneralRe: Interesting Stuff Pin
Alan Beasley1-Jun-10 7:08
Alan Beasley1-Jun-10 7:08 
GeneralRe: Interesting Stuff Pin
silverazure3-Jun-10 20:49
silverazure3-Jun-10 20:49 
GeneralRe: Interesting Stuff Pin
Alan Beasley3-Jun-10 21:43
Alan Beasley3-Jun-10 21:43 

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.