This article is compatible with the latest version of Silverlight for Windows Phone 7.
Watch video accompanying this article!
This article is Part 1 of the series WP7 Stock Quote Demo.
This is the first of series of articles describing the development of a Stock quoting and Alerting application for the Windows 7 Phone. I want to make it clear that this is strictly a demonstration application to show off the capabilities of the phone and the rich development environment that it provides. One of the key points is having Silverlight available to deliver a great look and feel that has become standard on IPhone apps. Silverlight is especially good as it lets your developers and graphic designers work in concert using Visual Studio and Expression Blend for the Phone. The version of Silverlight used on the phone is the Out of Browser so we cannot host Silverlight apps in the Phone’s browser. But this is only half the story as many apps will lend themselves to a Push model where the phone acts simply as a message receiver. In this scenario strong server side development skills are also required and this subject will be addressed in the last series of these articles.Some of the development features that I want to share are:
- Threading
- WebClient access
- MVVM light
- Thread communication using Messaging
- Isolated store
- Linq
- Behaviors
- MPN (Push which I discuss in Part 4 and 5)
Now many of these are beyond the needs of simple applications like your flashlight app but I wanted to show the power that is available as your feature set requirements rise. Stock quoting was chosen as the domain as it lends itself to using these features and I could get easy access to data using Yahoo Financial as the source of the quotes, however the data access is abstracted and therefore easily changed.
I will start of by building the User Experience(UX) and data access to display the quotes on the phone. In this mode the user is able to select the stocks they want to follow and set alerts that are fired when a threshold is exceeded. In the end, these alerts are the real value to the application. The interactive use should be minimized as the battery usage and bandwidth requirements are too great. We will then move the application logic to a push model where the alerts are fired from an Azure server application using Microsoft Push Notification (MPN).
The phone client that will build in Part 1 uses the MVVM pattern although you will notice code in the views (foo.xaml.cs) and I need to explain my philosophy here. I try and use MVVM as much as possible but there are cases where I am doing pure UX changes and in these cases I will leave the code in the Page behind. A related issue is that the version of Silverlight on the phone seems to be mostly 3 and as such there is no Commanding. As a result I make extensive use of Laurent Bugnion’s wonderful MVVM Light which delivers the same functionality. I originally started this project as a pure Silverlight 4 application where I abstracted the data access using MEF however sadly MEF is not available for the phone as Reflection.Emit was omitted from the CLR for the Phone. However the rest of the port to the WP7 was painless after I got used to not having a mouse!
Overall the design looks like:
Step 1.
I will assume that you installed the bits for WP7 development, if not there is great information at http://www.silverlight.net/getstarted/devices/windows-phone/ to get you started. So I will assume that you can create a Windows 7 Phone List project. Out of the box this project has the ViewModel plumbing that we need as the ViewModel is instantiated as a static class in App.cs:
public partial class App : Application
{
// Static view model
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
This is set as the DataContext for view in MainPage.cs
// When page is navigated to, set data context
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Set the data context of the listbox control to the sample data
if (DataContext == null)
DataContext = App.ViewModel;
}
Now we add Laurent’s MVVM Light which can be downloaded from: http://mvvmlight.codeplex.com/. This will provide us with a Messaging class for inter-thread communications and Commanding that is missing in this version of Silverlight on the phone. This Commanding is integral for the MVVM pattern to be used So we need to add the following three references:
Out of the box the ListBox in MainPage.xaml is bound to an ObservableCollection of ItemViewModel and here is where we start to make some changes. The first thing that we do is add a Quote Model that will represent the properties we want to display to the user:
public class Quote : INotifyPropertyChanged
{
string _Symbol;
DateTime _Time;
double _Trade;
double _volume;
double _Bid;
double _Ask;
bool _direction;
double _lastTrade;
double _TargetPrice;
double _DayHi;
double _DayLow;
double _YrHi;
double _YrLow;
public string Symbol
{
get
{
return _Symbol;
}
set
{
_Symbol = value;
RaisePropertyChanged("Symbol");
}
}
public DateTime Time
{
get { return _Time; }
set
{
_Time = value;
RaisePropertyChanged("Time");
}
}
public double Trade
{
get { return _Trade; }
set
{
_Trade = value;
RaisePropertyChanged("Trade");
if (_lastTrade < _Trade)
Direction = true;
_lastTrade = Trade;
}
}
public double Volume
{
get { return _volume; }
set
{
_volume = value;
RaisePropertyChanged("Volume");
}
}
public double Bid
{
get { return _Bid; }
set
{
_Bid = value;
RaisePropertyChanged("Bid");
}
}
public double Ask
{
get { return _Ask; }
set
{
_Ask = value;
RaisePropertyChanged("Ask");
}
}
public double DayHi
{
get { return _DayHi; }
set
{
_DayHi = value;
RaisePropertyChanged("DayHi");
}
}
public double DayLow
{
get { return _DayLow; }
set
{
_DayLow = value;
RaisePropertyChanged("DayLow");
}
}
public double YrLow
{
get { return _YrLow; }
set
{
_YrLow = value;
RaisePropertyChanged("YrLow");
}
}
public double YrHi
{
get { return _YrHi; }
set
{
_YrHi = value;
RaisePropertyChanged("YrHi");
}
}
public bool Direction
{
get { return _direction; }
set
{
_direction = value;
RaisePropertyChanged("Direction");
}
}
public double TargetPrice
{
get { return _TargetPrice; }
set
{
_TargetPrice = value;
RaisePropertyChanged("TargetPrice");
}
}
public string CompareGT { get; set; }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if ((PropertyChanged != null))
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Note that Quote extends INotifyPropertyChanged so that our quote object can participate in two-way data-binding with the XAML. As in the out of the box solution the Quotes will be contained in ObservableCollection and as a result additions and removals from the list will result in UX changes.
Now we gut the generated MainViewModel and only leave the ObservableCollection of Quotes so it looks like:
namespace WindowsPhoneListApplication3
{
public class MainViewModel:ViewModelBase
{
public ObservableCollection<Quote> Items { get; private set; }
}
}
Step 2.
As shown in Figure 1 we want to build up a UX that is different from what comes out of the box but as we proceed we will always have sample data to view at design time whether we are in Blend or the VS Designer. The MainViewModelSampleData.xaml file will supply this:
<local:MainViewModel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StockQuotesClient"
>
<local:MainViewModel.quotesList>
<local:Quote Symbol="aapl" Time="4:56:12 PM" Trade="250" ></local:Quote>
<local:Quote Symbol="msft" Time="4:56:12 PM" Trade="25" ></local:Quote>
<local:Quote Symbol="csco" Time="4:56:12 PM" Trade="22" ></local:Quote>
<local:Quote Symbol="ibm" Time="4:56:12 PM" Trade="130" ></local:Quote>
<local:Quote Symbol="rim" Time="4:56:12 PM" Trade="25" ></local:Quote>
<local:Quote Symbol="c" Time="4:56:12 PM" Trade="4" ></local:Quote>
</local:MainViewModel.quotesList>
</local:MainViewModel>
Then the line of code in the generated XAML for the MainPage.xaml will bind the CLR quote objects that are produced by the XAML in this file to our design surface:
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
As a result our Design view will look something like this:
The first thing we do is change the XAML for Application Title as follows:
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle" Text="Yahoo Quotes" Style="{StaticResource PhoneTextNormalStyle}" FontSize="36"/>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="textBlock" Text="Enter Symbol" Width="150" HorizontalAlignment="Left" TextAlignment="Center" FontSize="24"/>
<TextBox x:Name="Sym" Width="100" />
<Button x:Name="AddSymbol" Width="100" Content="Add" Height="70" >
</Button>
</StackPanel>
</StackPanel>
This just add the TextBox called Sym that will allow the entry of a symbol and the Button:
We now want to wire to the ViewModel which is typically done in Silverlight and WPF with Commanding,unfortunately the version of Silverlight on the phone does not include commanding but MVVMLight comes to the rescue and we modify the XAML using Blend. It is important that your XAML remains Blendable as only then can your developers and designers work as a team and produce the great UX that we need to compete.
In the MainViewModel I defined the GetSymbolCommand as a property that returns an ICommand and is set using MVVMLight’s RelayCommand as follows:
public ICommand GetSymbolCommand
{
get
{
if (_GetSymbolCommand == null)
_GetSymbolCommand = new RelayCommand<object>(GetSymbol);
return _GetSymbolCommand;
}
}
The GetSymbol is an Action delegate that will accept as input the Text property of the Sym TextBox. It will then create a new Quote object and add it to quotesList. This is a ObservableCollection so we can add and remove from it and have the ListView that it is bound to updated
public void GetSymbol(object input)
{
string symbol = input as string;
if (symbol.Length == 0)
return;
Quote q = new Quote() { Symbol = symbol };
quotesList.Add(q);
}
When the project is opened in Blend click on the Assets button, then Behaviors and you see:
Grab the MVVM light EventToCommand and drop it on the AddSymbol button in the Objects and TimeLine .Now click on the newly added EventToCommand then click on Advanced Properties
These actions will result in our XAML being modified to look like :
<Button x:Name="AddSymbol" Width="70" Content="Add" >
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding GetSymbolCommand}" CommandParameter="{Binding Text, ElementName=Sym}"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</Button>
Now when the user enters a symbol the GetSymbol method of the MainViewModel is called:
So Part 1 just get us started and in Part 2 we enhance the UX to produce the Look we want along with the animations.