Lottery Numbers Picker (Sharing Animations)

0
299

Lottery Numbers Picker helps you choose potentially winning numbers to use when you buy a lottery ticket. First, you can tell it how many numbers it needs to select, what the range of the numbers should be, and whether duplicate numbers are allowed. Then, Lottery Numbers Picker selects the numbers with a fun animation that mimics a machine filled with percolating lottery balls (the kind you see on TV).

The Main Page

Lottery Numbers Picker has a main page, a settings page, and the standard about page.

The User Interface

Listing 16.1 contains the XAML for the main page.

LISTING 16.1 MainPage.xaml—The Main User Interface for Lottery Numbers Picker

[code]

<phone:PhoneApplicationPage
x:Class=”WindowsPhoneApp.MainPage”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
xmlns:local=”clr-namespace:WindowsPhoneApp”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeNormal}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”Portrait” shell:SystemTray.IsVisible=”True”>
<!– The application bar –>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar>
<shell:ApplicationBarIconButton Text=”pick”
IconUri=”/Shared/Images/appbar.play.png”
Click=”PickButton_Click”/>
<shell:ApplicationBarIconButton Text=”settings”
IconUri=”/Shared/Images/appbar.settings.png”
Click=”SettingsButton_Click”/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text=”about” Click=”AboutMenuItem_Click”/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
<!– Add one storyboard to the page’s resource dictionary –>
<phone:PhoneApplicationPage.Resources>
<!– This storyboard is applied to each chosen ball, one at a time –>
<Storyboard x:Name=”ChosenBallStoryboard”
Completed=”ChosenBallStoryboard_Completed”>
<!– Makes the chosen ball “blow” upward from the bottom –>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=”(Canvas.Top)”>
<LinearDoubleKeyFrame KeyTime=”0:0:0” Value=”728”/>
<EasingDoubleKeyFrame KeyTime=”0:0:1.5” Value=”100”>
<EasingDoubleKeyFrame.EasingFunction>
<ElasticEase Oscillations=”1” Springiness=”6” EasingMode=”EaseOut”/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<!– Makes the chosen ball slide to the left, once at the top –>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=”(Canvas.Left)”>
<LinearDoubleKeyFrame KeyTime=”0:0:0” Value=”424”/>
<LinearDoubleKeyFrame KeyTime=”0:0:1.2” Value=”424”/>
<EasingDoubleKeyFrame x:Name=”FinalLeftKeyFrame” KeyTime=”0:0:2”>
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase EasingMode=”EaseOut” Bounces=”1” Bounciness=”8”/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<!– Spins the chosen ball while it moves up and left, and end upright –>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=
“(local:LotteryBall.RenderTransform).(RotateTransform.Angle)”>
<LinearDoubleKeyFrame KeyTime=”0:0:0” Value=”0”/>
<EasingDoubleKeyFrame KeyTime=”0:0:1.2” Value=”360”>
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase Bounces=”10”/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime=”0:0:2” Value=”0”>
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase EasingMode=”EaseOut” Bounces=”1” Bounciness=”8”/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</phone:PhoneApplicationPage.Resources>
<!– Prevent off-screen visuals from appearing during a page transition –>
<phone:PhoneApplicationPage.Clip>
<RectangleGeometry Rect=”0,0,480,728”/>
</phone:PhoneApplicationPage.Clip>
<Canvas>
<!– Mini-header –>
<TextBlock Text=”LOTTERY NUMBERS PICKER” Margin=”24,16,0,12”
Style=”{StaticResource PhoneTextTitle0Style}”/>
<!– Chosen balls get dynamically added to this canvas –>
<Canvas x:Name=”ChosenBallsCanvas”/>
<!– A canvas filled with percolating plain balls –>
<Canvas>
<local:LotteryBall Percolating=”True” Canvas.Top=”630”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”40” Canvas.Top=”635”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”80” Canvas.Top=”630”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”120” Canvas.Top=”635”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”160” Canvas.Top=”630”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”200” Canvas.Top=”635”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”240” Canvas.Top=”630”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”280” Canvas.Top=”635”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”320” Canvas.Top=”630”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”360” Canvas.Top=”635”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”5” Canvas.Top=”650”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”45” Canvas.Top=”655”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”85” Canvas.Top=”650”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”125” Canvas.Top=”655”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”165” Canvas.Top=”650”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”205” Canvas.Top=”655”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”245” Canvas.Top=”650”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”285” Canvas.Top=”655”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”325” Canvas.Top=”650”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”365” Canvas.Top=”655”/>
<local:LotteryBall Percolating=”True” Canvas.Top=”670”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”40” Canvas.Top=”675”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”80” Canvas.Top=”670”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”120” Canvas.Top=”675”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”160” Canvas.Top=”670”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”200” Canvas.Top=”675”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”240” Canvas.Top=”670”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”280” Canvas.Top=”675”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”320” Canvas.Top=”670”/>
<local:LotteryBall Percolating=”True” Canvas.Left=”360” Canvas.Top=”675”/>
</Canvas>
<!– The container of balls –>
<Rectangle Fill=”{StaticResource PhoneAccentBrush}” Canvas.Top=”153”
Width=”421” Height=”600” Opacity=”.5”/>
</Canvas>
</phone:PhoneApplicationPage>

[/code]

Notes:

  • The single storyboard in Listing 16.1 doesn’t have Storyboard.TargetName assigned. That’s because this storyboard is dynamically assigned to each new ball that reveals a lottery number, as you’ll see in the code-behind. It contains three animations: one to move the ball upward along the right side of the screen, one to move it over to the left, and one to spin it the whole time. The animations are given various easing functions with various properties set to give a lifelike appearance of each ball being blown into place.
  • Although the chosen lottery balls are dynamically added to ChosenBallsCanvas, the percolating balls in the ball machine have been statically added to the next canvas in hardcoded locations. Each ball is represented by a LotteryBall user control, shown in the next section. Because their custom Percolating property is set to true, these balls bounce erratically.

The Code-Behind

Listing 16.2 contains the code-behind for the main page.

LISTING 16.2 MainPage.xaml.cs—The Code-Behind for Lottery Numbers Picker’s Main Page

[code]

using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Microsoft.Phone.Controls;
namespace WindowsPhoneApp
{
public partial class MainPage : PhoneApplicationPage
{
Random random = new Random();
List<int> chosenNumbers = new List<int>();
LotteryBall mostRecentBall;
IApplicationBarIconButton pickButton;
public MainPage()
{
InitializeComponent();
this.pickButton = this.ApplicationBar.Buttons[0]
as IApplicationBarIconButton;
}
void ShowNewBall()
{
// Create a new ball with the correct chosen number.
// The RotateTransform is there so the spinning animation works.
this.mostRecentBall = new LotteryBall {
RenderTransform = new RotateTransform(),
Number = this.chosenNumbers[this.ChosenBallsCanvas.Children.Count]
};
// Assign the storyboard to this new ball
Storyboard.SetTarget(this.ChosenBallStoryboard, this.mostRecentBall);
// Adjust the final horizontal position of the ball based on which one it is
this.FinalLeftKeyFrame.Value = this.mostRecentBall.Width *
this.ChosenBallsCanvas.Children.Count;
// Add the new ball to the canvas
this.ChosenBallsCanvas.Children.Add(this.mostRecentBall);
// Start animating
this.ChosenBallStoryboard.Begin();
}
void ChosenBallStoryboard_Completed(object sender, EventArgs e)
{
// The storyboard must be stopped before its
// target is changed again inside ShowNewBall
this.ChosenBallStoryboard.Stop();
// Manually position the ball in the same spot where the animation left it
Canvas.SetTop(this.mostRecentBall, 100);
Canvas.SetLeft(this.mostRecentBall,
this.mostRecentBall.Width * (this.ChosenBallsCanvas.Children.Count – 1));
// Keep going until enough balls have been chosen
if (this.ChosenBallsCanvas.Children.Count < Settings.NumBalls.Value)
ShowNewBall();
else
this.pickButton.IsEnabled = true;
}
// Application bar handlers
void PickButton_Click(object sender, EventArgs e)
{
this.pickButton.IsEnabled = false;
this.chosenNumbers.Clear();
this.ChosenBallsCanvas.Children.Clear();
// Pick all the numbers
for (int i = 0; i < Settings.NumBalls.Value; i++)
{
// If no duplicate numbers are allowed, keep
// picking until the number is unique
int num;
do
{
num = this.random.Next(Settings.MinNumber.Value,
Settings.MaxNumber.Value + 1);
}
while (!Settings.AllowDuplicates.Value &&
this.chosenNumbers.Contains(num));
this.chosenNumbers.Add(num);
}
// Sort the chosen numbers in increasing numeric order
this.chosenNumbers.Sort();
// Reveal the first ball
ShowNewBall();
}
void SettingsButton_Click(object sender, EventArgs e)
{
this.NavigationService.Navigate(new Uri(“/SettingsPage.xaml”,
UriKind.Relative));
}
void AboutMenuItem_Click(object sender, EventArgs e)
{
this.NavigationService.Navigate(new Uri(
“/Shared/About/AboutPage.xaml?appName=Lottery Numbers Picker”,
UriKind.Relative));
}
}
}

[/code]

Notes:

  • Inside ShowNewBall, a number is assigned to the new ball from the chosenNumbers list that is filled inside PickButton_Click. The number of children in ChosenBallsCanvas can be used as the list’s index before the new ball is added to the canvas because the number is 0 for the first ball, 1 for the second ball, and so on.
  • Rather than calling Storyboard.SetTargetName (which effectively sets the Storyboard.TargetName attachable property), the code calls Storyboard.SetTarget to assign the target element to the storyboard. This is easier to use in C#, because you can simply pass it the instance of the element to animate even when it doesn’t have a name.
  • The storyboard’s Completed event handler (ChosenBallStoryboad_ Completed) calls ShowNewBall to repeat the creation and animation of a new ball until the correct number of balls have been shown. It must stop the storyboard, rather than having it remain in a filling state, because otherwise the next call to Storyboard.SetTarget inside ShowNewBall would fail. You cannot change a storyboard’s target unless it is completely stopped. Because the storyboard is stopped, the just-animated ball is manually given its ending position. Without this code, the ball would snap back to its pre-animation position. An alternative approach would be to get the ball’s position before stopping the storyboard and then setting it to that position after stopping it.

The LotteryBall User Control

The LotteryBall user control used by the main page represents each circle with an optional number centered on it. The XAML for this user control is shown in Listing 16.3. The most interesting part of this XAML is that the control uses a TransformGroup for its translation and rotation rather than a CompositeTransform. This is because a CompositeTransform performs rotation before translation, but the functionality of this control requires that the rotation happens after the translation. Therefore, TransformGroup is used with its TranslateTransform child placed before the RotateTransform child.

LISTING 16.3 LotteryBall.xaml—The User Interface for the LotteryBall User Control

[code]

<UserControl x:Class=”WindowsPhoneApp.LotteryBall”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
FontFamily=”Segoe WP Black” FontSize=”{StaticResource PhoneFontSizeLarge}”
Width=”53” Height=”53” RenderTransformOrigin=”.5,.5”>
<UserControl.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name=”TranslateTransform”/>
<RotateTransform x:Name=”RotateTransform”/>
</TransformGroup>
</UserControl.RenderTransform>
<Grid>
<Ellipse Fill=”{StaticResource PhoneForegroundBrush}”/>
<TextBlock x:Name=”NumberTextBlock” Margin=”1,0,0,3”
Foreground=”{StaticResource PhoneBackgroundBrush}”
HorizontalAlignment=”Center” VerticalAlignment=”Center”/>
</Grid>
</UserControl>

[/code]

Listing 16.4 contains the code-behind for this user control.

LISTING 16.4 LotteryBall.xaml.cs—The Code-Behind for the LotteryBall User Control

[code]

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace WindowsPhoneApp
{
public partial class LotteryBall : UserControl
{
int number;
bool percolating;
Storyboard percolatingStoryboard;
DoubleAnimation percolatingAnimation;
Random random = new Random();
public LotteryBall()
{
InitializeComponent();
}
public int Number
{
get { return this.number; }
set
{
this.NumberTextBlock.Text = value.ToString();
this.number = value;
}
}
public bool Percolating
{
get { return this.percolating; }
set
{
this.percolating = value;
if (this.percolating)
{
// Create a new single-animation storyboard
this.percolatingStoryboard = new Storyboard();
this.percolatingAnimation = new DoubleAnimation { AutoReverse = true };
this.percolatingAnimation.EasingFunction = new QuadraticEase {
EasingMode = EasingMode.EaseInOut };
this.percolatingStoryboard.Children.Add(this.percolatingAnimation);
// Assign the storyboard to this instance’s TranslateTransform and
// animate its Y property to create a “bounce”
Storyboard.SetTarget(this.percolatingStoryboard,
this.TranslateTransform);
Storyboard.SetTargetProperty(this.percolatingStoryboard,
new PropertyPath(“Y”));
// When the “bounce” completes, choose new random values and start
// it again. Repeat indefinitely.
this.percolatingStoryboard.Completed += delegate(object s, EventArgs e)
{
Randomize();
this.percolatingStoryboard.Begin();
};
// Choose random values related to the animation
// and start it for the first time
Randomize();
percolatingStoryboard.Begin();
}
}
}
void Randomize()
{
// Vary the distance and duration of the bounce
this.percolatingAnimation.To = this.random.Next(20, 60) * -1;
this.percolatingAnimation.Duration = TimeSpan.FromMilliseconds(
this.random.Next(50, 200));
// Very the angle of the bounce
this.RotateTransform.Angle = this.random.Next(0, 90) * -1;
}
}
}

[/code]

Notes:

  • This user control exposes two properties: Number, which displays the input number on top of the ellipse, and Percolating, which makes the ball bounce erratically when set to true. In this app, only Number is set for the chosen balls and only Percolating is set for the balls inside the machine.
  • The erratic bouncing of a percolating ball is enabled by a short random bounce storyboard that, when completed, is given new random values before beginning again. This storyboard and animation is created entirely in C#, which has not previously been seen in this book. The code mirrors what you would write in XAML, except that the adding of the animation to the storyboard is more explicit (with a call to Children.Add), and the property path used for specifying the target property is also more explicit. As with Listing 16.2, SetTarget is used to directly associate the storyboard with the target object.
  • Inside Randomize, the length of each bounce is allowed to vary from 20 to 59 pixels upward (in the negative direction), and the duration of the upward motion is allowed to vary from 50 to 199 milliseconds. To prevent each ball from bouncing straight up, the angle of the user control is randomly set to a range of 0 to –90°. Although the angle is not part of the animation, it affects the animation because it changes the meaning of translating upward. This is why it’s important that the RotateTransform appears after the TranslateTransform in Listing 16.3. Although the ball is translated upward from its origin, the whole coordinate space is then rotated, causing the ball to translate at whatever angle has been chosen. (If the rotation happened first, it would have no effect on the symmetric plain circle.) The range of 0 to –90° is chosen so the balls don’t ever bounce to the right and break the illusion that they are contained inside the machine.

The Settings Page

The Lottery Numbers Picker app uses the following settings defined in Settings.cs:

[code]

public static class Settings
{
public static readonly Setting<int> NumBalls =
new Setting<int>(“NumBalls”, 5);
public static readonly Setting<int> MinNumber =
new Setting<int>(“MinNumber”, 1);
public static readonly Setting<int> MaxNumber =
new Setting<int>(“MaxNumber”, 56);
public static readonly Setting<bool> AllowDuplicates =
new Setting<bool>(“AllowDuplicates”, false);
}

[/code]

The settings page, shown in Figure 16.1, enables the user to adjust all these settings.

FIGURE 16.1 The settings page contains a check box and three sliders.
FIGURE 16.1 The settings page contains a check box and three sliders.

The User Interface

Listing 16.5 contains the XAML for the settings page.

LISTING 16.5 SettingsPage.xaml—The User Interface for Lottery Numbers Picker’s Settings Page

[code]

<phone:PhoneApplicationPage
x:Class=”WindowsPhoneApp.SettingsPage”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
xmlns:local=”clr-namespace:WindowsPhoneApp”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeNormal}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”PortraitOrLandscape” shell:SystemTray.IsVisible=”True”>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– The standard settings header –>
<StackPanel Grid.Row=”0” Style=”{StaticResource PhoneTitlePanelStyle}”>
<TextBlock Text=”SETTINGS” Style=”{StaticResource PhoneTextTitle0Style}”/>
<TextBlock Text=”lottery numbers picker”
Style=”{StaticResource PhoneTextTitle1Style}”/>
</StackPanel>
<ScrollViewer Grid.Row=”1”>
<Grid Margin=”{StaticResource PhoneMargin}”>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<!– Allow duplicate numbers –>
<CheckBox x:Name=”AllowDuplicatesCheckBox” HorizontalAlignment=”Left”
Content=”Allow duplicate numbers” local:Tilt.IsEnabled=”True”/>
<!– Number of balls –>
<TextBlock Grid.Row=”1” Text=”Number of balls” Margin=”12,16,0,8”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
<TextBlock x:Name=”NumBallsTextBlock” Grid.Row=”1” Margin=”0,16,23,0”
HorizontalAlignment=”Right”
Text=”{Binding Value, ElementName=NumBallsSlider}”/>
<Slider x:Name=”NumBallsSlider” Grid.Row=”2” Minimum=”1” Maximum=”8”
Tag=”{Binding ElementName=NumBallsTextBlock}”
ValueChanged=”Slider_ValueChanged” />
<!– Smallest possible number –>
<TextBlock Grid.Row=”3” Text=”Smallest possible number” Margin=”12,7,0,8”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
<TextBlock x:Name=”MinNumberTextBlock” Grid.Row=”3” Margin=”0,7,23,0”
HorizontalAlignment=”Right”
Text=”{Binding Value, ElementName=MinNumberSlider}”/>
<Slider x:Name=”MinNumberSlider” Grid.Row=”4” Minimum=”0” Maximum=”98”
Tag=”{Binding ElementName=MinNumberTextBlock}”
ValueChanged=”Slider_ValueChanged” />
<!– Largest possible number –>
<TextBlock Grid.Row=”5” Text=”Largest possible number” Margin=”12,7,0,8”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
<TextBlock x:Name=”MaxNumberTextBlock” Grid.Row=”5” Margin=”0,7,23,0”
HorizontalAlignment=”Right”
Text=”{Binding Value, ElementName=MaxNumberSlider}”/>
<Slider x:Name=”MaxNumberSlider” Grid.Row=”6” Minimum=”1” Maximum=”99”
Tag=”{Binding ElementName=MaxNumberTextBlock}”
ValueChanged=”Slider_ValueChanged” />
</Grid>
</ScrollViewer>
</Grid>
</phone:PhoneApplicationPage>

[/code]

Notes:

  • AllowDuplicatesCheckBox is aligned to the left to prevent accidental tapping and over-tilting if the right side of the screen is tapped.
  • NumBallsSlider enables a range from 1 to 8, MinNumberSlider enables a range from 0 to 98, and MaxNumberSlider enables a range from 1 to 99. Each slider has a corresponding text block that binds its text to the slider’s current value. This only works appropriately due to logic in code-behind in an event handler for each slider’s ValueChanged event.

The Code-Behind

Listing 16.6 contains the code-behind for the settings page.

LISTING 16.6 SettingsPage.xaml.cs—The Code-Behind for Lottery Numbers Picker’s Settings Page

[code]

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
namespace WindowsPhoneApp
{
public partial class SettingsPage : PhoneApplicationPage
{
bool initialized = false;
public SettingsPage()
{
InitializeComponent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Save the chosen settings
Settings.AllowDuplicates.Value =
this.AllowDuplicatesCheckBox.IsChecked.Value;
Settings.NumBalls.Value = (int)this.NumBallsSlider.Value;
Settings.MinNumber.Value = (int)this.MinNumberSlider.Value;
Settings.MaxNumber.Value = (int)this.MaxNumberSlider.Value;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Respect the saved settings
this.AllowDuplicatesCheckBox.IsChecked = Settings.AllowDuplicates.Value;
this.NumBallsSlider.Value = Settings.NumBalls.Value;
this.MinNumberSlider.Value = Settings.MinNumber.Value;
this.MaxNumberSlider.Value = Settings.MaxNumber.Value;
this.initialized = true;
}
void Slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
Slider slider = sender as Slider;
// Round the value so it’s a whole number even when the slider is dragged
slider.Value = Math.Round(slider.Value);
// Don’t do anything until all initial values have been set
if (this.initialized)
{
// Don’t allow min to be higher than max
if (this.MinNumberSlider.Value > this.MaxNumberSlider.Value)
this.MaxNumberSlider.Value = this.MinNumberSlider.Value;
// If the range is too small, auto-allow duplicates
if (this.MinNumberSlider.Value >=
this.MaxNumberSlider.Value – this.NumBallsSlider.Value)
{
this.AllowDuplicatesCheckBox.IsChecked = true;
this.AllowDuplicatesCheckBox.IsEnabled = false;
}
else
{
this.AllowDuplicatesCheckBox.IsEnabled = true;
}
}
}
bool IsMatchingOrientation(PageOrientation orientation)
{
return ((this.Orientation & orientation) == orientation);
}
}
}

[/code]

Slider_ValueChanged forces each slider’s value to an integral value, because it could otherwise become fractional when the user drags the slider. (When this Math.Round assignment actually changes the value, it causes Slider_ValueChanged to be called again. When the value is already a whole number, this does not happen, which is important for preventing an infinite loop.) This method also guards against conditions that would cause the app to crash or hang, enforcing a valid relationship between the minimum number and the maximum number.

The Finished Product

Lottery Numbers Picker (Sharing Animations)