Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight Prize Wheel Animation Using Custom Circular ListBox Control

0.00/5 (No votes)
25 May 2010 1  
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.

<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:

<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().

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.

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:

<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().

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.

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.

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 has no explicit license attached to it but may contain 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