The Jot
program I’ll be discussing next is one of three programs in this article that you might find useful on the phone in daily life. The idea
for it arose out of the QuickNotes program described later in this article. QuickNotes is basically a big TextBox control but it retains the contents of this TextBox
in isolated storage. Every time you open the program, you get the same
text you left in there the last time you used the program. You can add
text and delete text. It’s good for taking quick notes (as the program
name suggests) because you don’t have to load or save any files. That’s
all done automatically.
But I’ll discuss QuickNotes in
more detail at the end of this chapter. For now, the Jot program is
similar and rather easier to use because it doesn’t require a virtual or
actual keyboard. You just use your finger for writing or drawing.
As you’ll see, QuickNotes
makes do with just one text document because you can easily scroll
through and insert text wherever you want. But Jot is more
canvas-oriented, and the fixed size of this canvas seemed to imply that
the program should support multiple pages for multiple canvases.
Jot displays finger input using a class named InkPresenter, which originated with tablet interfaces. InkPresenter derives from the Canvas panel, which means you could use the Children property of InkPresenter to design a background image (a yellow legal pad, for example). Or you can ignore the Canvas part of InkPresenter.
InkPresenter exists primarily for displaying “ink,” which is represented as multiple series of connected short lines—polylines in graphics speak but called strokes in this context.
A particular point on the display surface is a StylusPoint, a structure defined in the System.Windows.Input namespace with X and Y properties as well as a PressureFactor for devices that support pressure. (Windows Phone 7 does not support touch pressure.)
When you draw on a screen with a finger (on touch screens) or stylus
(on tablets), you might create a bunch of crazy curves, but regardless
how complex the curves are, they are always represented by a collection
of StylusPoint objects that together mimic the complex curve. This collection of StylusPoint objects representing a continuous line is encapsulated in a Stroke, a class defined in the System.Windows.Ink namespace. The Stroke object encapsulates not only the points of this line, but also its color and width with these two properties:
A Stroke
is a continuous line created when the user touches the screen, moves
the finger, and lifts. Touching the screen again begins another Stroke. Multiple StrokeStrokeCollection. And that’s what the InkPresenter stores: InkPresenter defines a Strokes property of type StrokeCollection and renders those strokes, each of which forms a continuous line. objects are stored in a
My Jot program supports multiple pages, so it will need yet another collection to store a StrokeCollection for each page.
You need to know all this up front because I’m going to begin my discussion of the Jot program with the application
settings. The whole idea of the program is that it always brings you
back to where you last left off. Jot doesn’t need any transient data for
tombstoning because it treats tombstoning the same way as normal program launching and closing. Jot needs to use isolated
storage to save the same data when it’s deactivated (tombstoned) as
when it’s closed, and it needs to load this data when it’s both launched
and activated (that is, revived after tombstoning).
The application settings for Jot are encapsulated in a class specifically for that purpose called JotAppSettings. An instance of JotAppSettings
is serialized and saved in isolated storage. The class also contains
methods to save and load the settings. The project needs a reference to
the System.Xml.Serialization library, and JotAppSettings needs several non-standard using directives for System.Collection.Generic, System.IO, System.IO.IsolatedStorage, and System.Xml.Serialization.
Here are the public properties of JotAppSettings that constitute application settings:
Example 1. Silverlight Project: Jot File: JotAppSettings.cs (excerpt)
public List<StrokeCollection> StrokeCollections {get; set; } public int PageNumber { set; get; } public Color Foreground { set; get; } public Color Background { set; get; } public int StrokeWidth { set; get; }
|
There is one StrokeCollection for each page that Jot displays; hence the program needs to save a collection of StrokeCollection objects. The program initially displays a particular page indicated by PageNumber.
The first time Jot is run, it picks up the Foreground and Background
colors from the system theme. However, the program implements an option
to swap those colors for drawing purposes, under the assumption that
you might prefer a white-on-black theme for most of the phone, but
black-on-white drawing for Jot. For that reason, it saves and loads
explicit colors. The StokeWidth property starts out as 3 (the default with InkPresenter) but can be set by the user to 1 or 5 instead.
I tried using the IsolatedStorageSettings class to save these items, but I couldn’t get it to work, so I switched to the regular isolated storage facility. Here’s the Save method:
Example 2. Silverlight Project: Jot File: JotAppSettings.cs (excerpt)
public void Save() { IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication(); IsolatedStorageFileStream stream = iso.CreateFile("settings.xml"); StreamWriter writer = new StreamWriter(stream);
XmlSerializer ser = new XmlSerializer(typeof(JotAppSettings)); ser.Serialize(writer, this);
writer.Close(); iso.Dispose(); }
|
The Save method creates (or re-creates) a file in the program’s isolated storage named settings.xml, obtains a StreamWriter associated with that file, and then uses the XmlSerializer class to serialize this particular instance of the JotAppSettings class.
The Load method is static because it must create an instance of JotAppSettings
by deserializing the file in isolated storage. If that file doesn’t
exist—which means the program is being run for the first time—then it
simply creates a new instance.
Example 3. Silverlight Project: Jot File: JotAppSettings.cs (excerpt)
public static JotAppSettings Load() { JotAppSettings settings; IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication();
if (iso.FileExists("settings.xml")) { IsolatedStorageFileStream stream = iso.OpenFile("settings.xml", FileMode. Open); StreamReader reader = new StreamReader(stream);
XmlSerializer ser = new XmlSerializer(typeof(JotAppSettings)); settings = ser.Deserialize(reader) as JotAppSettings;
reader.Close(); } else { // Create and initialize new JotAppSettings object settings = new JotAppSettings(); settings.StrokeCollections = new List<StrokeCollection>(); settings.StrokeCollections.Add(new StrokeCollection()); }
iso.Dispose(); return settings; }
|
The constructor of the class sets some (but not all) of the properties to default values:
Example 4. Silverlight Project: Jot File: JotAppSettings.cs (excerpt)
public JotAppSettings() { this.PageNumber = 0; this.Foreground = (Color)Application.Current.Resources["PhoneForegroundColor"]; this.Background = (Color)Application.Current.Resources["PhoneBackgroundColor"]; this.StrokeWidth = 3; }
|
This constructor is called both when the Load
method explicitly creates a new instance when the program is run for
the first time, and when the file in isolated storage is deserialized.
In the latter case, the default values in the constructor are all
replaced. However, it’s a good idea to keep these settings in the
constructor in case you later add a new application setting to the
program. That setting will not be in the existing file in isolated
storage, but will be set to a default value in this constructor.
Originally I also put the initialization of the StrokeCollection collection in the constructor:
settings.StrokeCollections = new List<StrokeCollection>();
settings.StrokeCollections.Add(new StrokeCollection());
However, I discovered that the Deserialize method of XmlSerializer would then avoid creating a new List object and simply add to the one created in the constructor, leaving me with one new empty StrokeCollection in the List every time I ran the program! That’s why I moved this code to the Load method.
The Save and Load methods of JotAppSettings are called only from App.xaml.cs while handling the four PhoneApplicationService events .
These events signal when the program is being launched, deactivated,
activated, and closed. App.xaml.cs also exposes the application settings
as a public property:
Example 5. Silverlight Project: Jot File: App.xaml.cs (excerpt)
public partial class App : Application { // Application Settings public JotAppSettings AppSettings { set; get; }
. . .
private void Application_Launching(object sender, LaunchingEventArgs e) { AppSettings = JotAppSettings.Load(); }
private void Application_Activated(object sender, ActivatedEventArgs e) { AppSettings = JotAppSettings.Load(); }
private void Application_Deactivated(object sender, DeactivatedEventArgs e) { AppSettings.Save(); }
private void Application_Closing(object sender, ClosingEventArgs e) { AppSettings.Save(); } }
|
Within MainPage, all references to the properties that comprise application settings are through that AppSettings property of the App class.