Blograby

The Code-Behind

Listing 2.2 contains the code-behind, which must handle all the special features of this flashlight—strobe mode, SOS mode, and various colors.

LISTING 2.2 MainPage.xaml.cs—The Code-Behind for Flashlight

[code]

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
namespace WindowsPhoneApp
{
public partial class MainPage : PhoneApplicationPage
{
// Members for the two application bar buttons:
IApplicationBarIconButton sosButton;
IApplicationBarIconButton strobeButton;
// For the two special modes:
SolidColorBrush onBrush;
SolidColorBrush offBrush = new SolidColorBrush(Colors.Black);
DispatcherTimer strobeTimer = new DispatcherTimer();
DispatcherTimer sosTimer = new DispatcherTimer();
int sosStep;
// Remember the chosen color, for future app activations or launches:
Setting<Color> savedColor = new Setting<Color>(“SavedColor”, Colors.White);
// The current mode (Solid, Sos, or Strobe)
FlashlightMode mode = FlashlightMode.Solid;
public MainPage()
{
InitializeComponent();
// Assign application bar buttons to member fields, because this cannot be
// done by InitializeComponent:
this.sosButton = this.ApplicationBar.Buttons[0]
as IApplicationBarIconButton;
this.strobeButton = this.ApplicationBar.Buttons[1]
as IApplicationBarIconButton;
// Initialize the timer for strobe mode
this.strobeTimer.Interval = TimeSpan.FromSeconds(.1); // Not too fast!
this.strobeTimer.Tick += StrobeTimer_Tick;
// Initialize the timer for SOS mode
this.sosTimer.Interval = TimeSpan.Zero;
this.sosTimer.Tick += SosTimer_Tick;
// Attach the same Click handler to all menu items in the application bar
foreach (IApplicationBarMenuItem menuItem in this.ApplicationBar.MenuItems)
menuItem.Click += MenuItem_Click;
// Restore persisted color
this.onBrush = new SolidColorBrush(this.savedColor.Value);
this.BackgroundGrid.Background = onBrush;
}
// The menu item Click handler that changes the flashlight color
void MenuItem_Click(object sender, EventArgs e)
{
// Grab the text from the menu item to determine the desired color
string chosenColor = (sender as IApplicationBarMenuItem).Text;
// Use reflection to turn the color name (e.g. “red”) into an actual Color
Color c = (Color)typeof(Colors).GetProperty(chosenColor,
BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase).
GetValue(null, null);
// Persist this choice and set the background color
this.savedColor.Value = c;
this.onBrush = new SolidColorBrush(this.savedColor.Value);
this.BackgroundGrid.Background = onBrush;
}
// The Click handler for the strobe button
void StrobeButton_Click(object sender, EventArgs e)
{
// First, reset the current state to solid mode
FlashlightMode mode = this.mode;
RestoreSolidMode();
// If we were already in strobe mode, then this click
// cancels it and we are done
if (mode == FlashlightMode.Strobe)
return;
// Show a warning
MessageBoxResult result = MessageBox.Show(“Strobe lights can trigger “ +
“seizures for people with photosensitive epilepsy. “ +
“Are you sure you want to start the strobe light?”,
“Warning!”, MessageBoxButton.OKCancel);
// If the user agreed, change to strobe mode
if (result == MessageBoxResult.OK)
{
// Change the button icon, the mode, and start the timer
(sender as IApplicationBarIconButton).IconUri =
new Uri(“Images/cancel.png”, UriKind.Relative);
this.mode = FlashlightMode.Strobe;
this.strobeTimer.Start();
}
}
void StrobeTimer_Tick(object sender, EventArgs e)
{
// Toggle the background on every tick
if (this.BackgroundGrid.Background == this.onBrush)
this.BackgroundGrid.Background = this.offBrush;
else
this.BackgroundGrid.Background = this.onBrush;
}
// The Click handler for the SOS button
void SosButton_Click(object sender, EventArgs e)
{
// First, reset the current state to solid mode
FlashlightMode mode = this.mode;
RestoreSolidMode();
// If we were already in SOS mode, then this click
// cancels it and we are done
if (mode == FlashlightMode.Sos)
return;
// Change to SOS mode
// Change the button icon, the mode, a counter, and start the timer
(sender as IApplicationBarIconButton).IconUri =
new Uri(“Images/cancel.png”, UriKind.Relative);
this.mode = FlashlightMode.Sos;
this.sosStep = 0;
this.sosTimer.Start();
}
void SosTimer_Tick(object sender, EventArgs e)
{
// Toggle the background, but also adjust the time between each tick in
// order to make the dot-dot-dot-dash-dash-dash-dot-dot-dot pattern
switch (this.sosStep)
{
case 1: case 3: case 5: // Each dot in the first S
case 13: case 15: case 17: // Each dot in the second S
this.BackgroundGrid.Background = this.onBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(.2); // A short value
break;
case 7: case 9: case 11: // Each dash in the O
this.BackgroundGrid.Background = this.onBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(1); // A long value
break;
case 18: // The space between the end of one SOS
// and the beginning of the next one
this.BackgroundGrid.Background = this.offBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(1);
break;
default: // The space between each dot/dash
this.BackgroundGrid.Background = this.offBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(.2);
break;
}
// Cycle from 0 – 18
this.sosStep = (this.sosStep + 1) % 19;
}
// Reset the state associated with mode switches
void RestoreSolidMode()
{
this.strobeTimer.Stop();
this.sosTimer.Stop();
this.BackgroundGrid.Background = onBrush;
this.sosButton.IconUri = new Uri(“Images/sos.png”, UriKind.Relative);
this.strobeButton.IconUri = new Uri(“Images/strobe.png”, UriKind.Relative);
this.mode = FlashlightMode.Solid;
}
// All three modes
enum FlashlightMode
{
Solid,
Sos,
Strobe
}
}
}

[/code]

Notes:

DispatcherTimer and Other Time-Based Approaches for Executing Code

DispatcherTimer, used by Flashlight, is the most natural timer to use in a Silverlight app.You can start and stop it at any time, customize its frequency with its Interval property, and handle its Tick event to perform work at the chosen interval. Event handlers for Tick are guaranteed to be called on the UI thread, so code inside these handlers can manipulate elements on the page the same way this is done everywhere else. DispatcherTimer is not the only timer available, however.

The System.Threading namespace has a Timer class that provides similar functionality, but the callback you provide does not get called on the UI thread.With this mechanism, you need to partition any logic that updates the UI into a different method and use the page’s dispatcher to invoke it on the UI thread.Here’s an example:

[code]

void TimerCallback(object state)
{
// Call the DoTheRealWork method on the UI thread:
this.Dispatcher.BeginInvoke(DoTheRealWork);
}

[/code]

Unless your timer-based code has no need to update the UI, you should stick to using DispatcherTimer instead of Timer.

The Reactive Extensions for .NET also includes a mechanism for creating a sequence that produces each value at a timed interval (Microsoft.Phone.Reactive.Observable.Timer) but the apps in this book series avoid using Reactive Extensions for the sake of having easilyunderstood code.

Any of these timers can work great for the needs of Flashlight and apps like it, but they should not be used for animations.These timers are not in sync with the screen’s refresh rate, nor are they in sync with the Silverlight rendering engine. Instead,many animations should use the animations classes covered throughout Part II,“Transforms & Animations.”These classes could even be used in Flashlight instead of a timer.

Complex animations (such as physics-based animations) can use a static CompositionTarget.Rendering event that gets raised on every frame, regardless of the exact timing

Message Boxes

Flashlight uses a message box to show a standard warning that enables the user to cancel the action. On most platforms, using a message box to communicate information is indicative of a lazy programmer who doesn’t want to create a nicer-looking user interface. On Windows Phone, however, a message box is not only appropriate for many situations, but it has a lot of niceties that are hard to create on your own! As with the phone’s builtin apps, it animates in and out (with a flip), it dims and disables the rest of the screen (including the application bar), its buttons tilt when pressed, it automatically shows the status bar with a background that matches the message box background (regardless of the
app’s SystemTray.IsVisible setting), it makes a pleasant sound, and it vibrates. Naturally, it also respects the user’s theme and the phone’s orientation.

MessageBox contains two overloads of its static Show method. With one, you simply pass a single piece of text:

[code]MessageBox.Show(“This is the message.”);[/code]

FIGURE 2.7 The standard message box with no
caption and a single OK button.

As shown in Figure 2.7, the resultant message box shows the message with a single OK button. It looks odd because it
has no caption, so apps should not use this overload of Show.

FIGURE 2.8 The message box used by
Flashlight, shown in the context of the entire page.

The more functional overload of Show, used by Flashlight, enables you to set the text and caption, plus choose what
buttons you want with a value from the MessageBoxButton enumeration: OK (a single OK button) or OKCancel (two
buttons—OK and cancel). Figure 2.8 shows the message box created back in Listing 2.2.

Both overloads of Show return a MessageBoxResult enumeration value that indicates which button, if any, was tapped. The only supported values are OK and Cancel. The latter is returned if the user taps the cancel button or if the user simply dismisses the message box with the hardware Back button.

Unfortunately, MessageBox.Show does not support custom labels for the two buttons. The “ok” and “cancel” labels are
all you get. Built-in phone apps, on the other hand, often customize the “ok” label to be more specific to the task at
hand, such as “call” versus “don’t call” or “delete” versus “cancel.”

You can actually customize the text on the two message box buttons, but not with the MessageBox class. Instead, this functionality is hidden in an odd place—the Microsoft.Xna.Framework.GamerServices assembly! The Guide class in the
Microsoft.Xna.Framework.GamerServices namespace provides a pair of static methods that any app (XNA or Silverlight) can use without any special capabilities—BeginShowMessageBox and EndShowMessageBox. BeginShowMessageBox can be used as follows:

[code]

Guide.BeginShowMessageBox(“Title”,
“This is the message.”,
new string[] { “button 1”, “button 2” }, // 2 buttons with custom labels
0, // Button index that has focus
// (irrelevant for the phone)
MessageBoxIcon.None, // This is ignored
new AsyncCallback(OnMessageBoxClosed), // Callback to process result
null // Custom state given to callback
);

[/code]

The OnMessageBoxClosed callback, which uses EndShowMessageBox, can look as follows:

[code]

void OnMessageBoxClosed(IAsyncResult result)
{
// See which button was tapped (if any)
int? buttonIndex = Guide.EndShowMessageBox(result);
if (buttonIndex == 1)
// Perform action #1
else if (buttonIndex == 2)
// Perform action #2
else
// Message box was dismissed with the hardware back button
}

[/code]

Despite the fact that you pass an arbitrary list of button labels to BeginShowMessageBox, only one or two labels are supported because you can only have one or two buttons. When using your own labels, be sure to follow design guidelines by putting the positive OKstyle button on the left and the negative cancel-style button on the right.

special features of this flashlight
Exit mobile version