Using Studio for Windows Phone in Data Applications
The first release of Studio for Windows Phone comes packed with data visualization controls, making it the ideal toolset for mobile apps built around data. These controls give you more ways to view, analyze and even edit your data. Charts and gauges are great visualization controls that look sharp and scale nicely to any screen. If you have lots of tabular data to browse and edit on the fly, then perhaps a data grid is the best solution. That’s where ComponentOne FlexGrid will come in handy because there is no data grid control included in the standard set of Windows Phone controls, or in the toolkit. At first, you may think a data grid does not make sense on a mobile device, but as soon as you need to display more than one column of data in a bound fashion, you'll realize a data grid is what you need. Add on advanced features like editing, sorting and filtering on top without having to write all that code, and you will be glad you have a data grid in your toolbox. For an overview of all the new controls, check out my previous post announcing the release . This article introduces 3 data visualization controls in ComponentOne’s new Studio for Windows Phone and walks through building an application for tracking product inventory. The application will include C1FlexGrid for displaying and editing products with multiple fields, and C1SpeedometerGauge and C1Chart to show some information about a product on a second screen when drilled-down. Product Inventory Tracker Sample
Part 1: Using FlexGrid
In a new Windows Phone project, add a UserControl called GridView.xaml. This view will be used to display a tabular list of products using C1FlexGrid. The ComponentOne FlexGrid for Windows Phone has been remodeled to provide a user experience more fitting for the mobile world. You can interact with it by intuitively dragging and tapping. Double-tapping even brings a cell into edit mode. The editing is not inline as it would be in a Web or desktop application, but rather on a separate page much like editing is done in the Windows Phone settings. Other features like freezing and cell merging are supported, and FlexGrid has an API which makes sorting and filtering data easy, but we won’t get into that for this example. Drop a FlexGrid on the page and replace its XAML with this below:
<my:C1FlexGrid Name="c1FlexGrid1"
ItemsSource="{Binding Products}"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}">
<my:C1FlexGrid.Columns>
<my:Column Header="Id" Binding="{Binding ProductId}" Width="50" />
<my:Column Header="Name" Binding="{Binding ProductName, Mode=TwoWay}" Width="130" />
<my:Column Header="In Stock" Binding="{Binding InStock, Mode=TwoWay}" Width="110" />
<my:Column Header="Units" Binding="{Binding UnitsInStock, Mode=TwoWay}" />
<my:Column Header="Price" Binding="{Binding UnitPrice, Mode=TwoWay}" Format="c2" />
</my:C1FlexGrid.Columns>
</my:C1FlexGrid>
Notice I’m binding both the ItemsSource and SelectedItem to whatever my data context is (more on that later). Here I’m also defining the columns explicitly in XAML. For the quickest and simplest grid, you could just set AutoGenerateColumns = True and the ItemsSource. By defining my columns I can customize features such as widths and headers. I can also add my own custom columns to FlexGrid. For instance, I want to add a Hyperlink button column that allows the user to navigate to another page within my application. Add this XAML inside the C1FlexGrid.Columns tag:
<my:Column Header="Stats" Width="80">
<my:Column.CellTemplate>
<DataTemplate>
<HyperlinkButton NavigateUri="/Views/GraphView.xaml">
<HyperlinkButton.Background>
<ImageBrush Stretch="None" ImageSource="../Resources/ChartArea.png" />
</HyperlinkButton.Background>
</HyperlinkButton>
</DataTemplate>
</my:Column.CellTemplate>
</my:Column>
This column is created by defining a cell template and putting my custom elements inside (you can download the full sample below to get the images used). When clicked on, the NavigateUri takes us to the specified page by referring to the pages navigation service.
Freezing Columns
One of the neat features you get with FlexGrid is the ability to freeze (or fix) rows and columns. This feature can be very useful on the phone where user interaction is of the utmost importance. In this sample, we should freeze the first button column so that the user can always access it, therefore improving the user experience. To freeze the first column we do this in code:
c1FlexGrid1.Columns.Frozen = 1;
Editing Cells
FlexGrid comes with built-in editing capabilities. You just have to make sure that the binding on each column you want to edit is Mode=TwoWay. The user can double tap a cell to edit its value.
Part 2: Using Chart and Gauge
On a second UserControl, named GraphView.xaml, define 3 rows and add a TextBlock such as this:
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding SelectedProduct.ProductName}" />
</Grid>
The TextBlock will be used to display a value bound from our data context (more on that later). Next, add a C1SpeedometerGauge to the 2nd row. This control is part of ComponentOne Gauges for Windows Phone, a set of five radial and linear shaped gauges. With Gauges, you can define decorators such as tick marks, ranges and labels in XAML or code. Use simple properties to customize each decorator’s interval, location and appearance. Replace the following XAML to show some decorators inside the C1SpeedometerGauge:
<my:C1SpeedometerGauge Name="c1SpeedometerGauge1" Grid.Row="1"
Minimum="0" Maximum="100" BorderThickness="3"
Value="{Binding SelectedProduct.UnitsInStock}">
<my:C1GaugeRange From="0" To="30" Location="0.65" Fill="Red" Width="0.3" Opacity="0.5" />
<my:C1GaugeRange From="30" To="70" Location="0.65" Fill="Yellow" Width="0.3" Opacity="0.5" />
<my:C1GaugeRange From="70" To="100" Location="0.65" Fill="Green" Width="0.3" Opacity="0.5" />
<my:C1GaugeMark Interval="20" Location="0.95" />
<my:C1GaugeMark Interval="10" Location="0.95"/>
<my:C1GaugeMark Interval="5" Location="0.95"/>
<my:C1GaugeLabel Interval="20" Alignment="In" AlignmentOffset="40" FontSize="20" Location="1.22" />
</my:C1SpeedometerGauge>
Notice we are binding the Value of the gauge to another field from our data context. The decorators cannot be bound to in this manner because they are not in the Visual Tree. You can get around this by setting the data context explicitly on the gauge itself. Next, add a C1Chart below the gauge. In addition to supporting 30 different chart types, C1Chart gives you the advanced features you need, such as scrolling (or touch-sensitive panning), tap zoom, data labels, support for multiple axes and plot areas, etc. ComponentOne Chart for Windows Phone shares the same codebase as Chart for Silverlight, so most features are the same (except image export and trend lines in this first release). All of the chart elements, such as data series and axes, can be declared in XAML. Replace the XAML with this below:
<my1:C1Chart ChartType="Line" Grid.Row="2" Name="c1Chart1">
<my1:C1Chart.Data>
<my1:ChartData ItemsSource="{Binding SelectedProduct.History}">
<my1:XYDataSeries Label="Unit Sales" ValueBinding="{Binding Value}" XValueBinding="{Binding Date}"/>
</my1:ChartData>
</my1:C1Chart.Data>
<my1:C1Chart.View>
<my1:ChartView>
<my1:ChartView.AxisX>
<my1:Axis IsTime="True" />
</my1:ChartView.AxisX>
</my1:ChartView>
</my1:C1Chart.View>
<my1:C1ChartLegend Position="Top" />
</my1:C1Chart>
Notice that the ItemsSource and values on the data series are bound to my data context. Continue to the next part for the view model.
Part 3: The ViewModel
I worked a bit backwards since you normally start with the view model before writing the view, but long snippets of code can look scary so I put this at the end. Since this sample is very data oriented it works well with a (very basic) MVVM architecture. With XAML controls you can bind all UI elements to public properties on your view model, and with the help of data templates and commands, nearly eliminate all code-behind. This is a popular design, so I wanted to show creating and binding the controls in XAML above. The rest is just a quick let’s-get-to-the-point, I just want some product information randomly generated for my UI. So here it is:
public class MainViewModel
{
private List<Product> _products;
private Product _selectedProduct = new Product();
public MainViewModel()
{
//blank
}
public List<Product> Products
{
get
{
if (_products == null)
{
// generate some sample data
Random _random = new Random();
_products = new List<Product>();
for (int i = 0; i < 50; i++)
{
\_products.Add(new Product(i, "Product " + i, true, \_random.Next(1, 100), \_random.Next(i, 100) + \_random.NextDouble()));
}
foreach (Product p in _products)
{
for (int i = 0; i < 10; i++)
{
p.History.Add(new ProductSaleHistory(p.ProductId, DateTime.Now.AddDays(i), _random.Next(1, 20)));
}
}
}
return _products;
}
}
public Product SelectedProduct
{
get
{
return _selectedProduct;
}
set
{
_selectedProduct = value;
}
}
}
The Products collection returns a simple list of Products. The SelectedProduct gets and sets a Product, which is demonstrated when the chart and gauge know which row has been selected in the FlexGrid (we bound FlexGrid’s SelectedItem to this property). We also need to define the Product and ProductSaleHistory classes.
public class Product
{
private List<ProductSaleHistory> _history;
public Product()
{
//blank
}
public Product(int productId, string productName, bool inStock, double unitsInStock, double unitPrice)
{
ProductId = productId;
ProductName = productName;
InStock = inStock;
UnitsInStock = unitsInStock;
UnitPrice = unitPrice;
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public bool InStock { get; set; }
public double UnitsInStock { get; set; }
public double UnitPrice { get; set; }
public List<ProductSaleHistory> History
{
get
{
if (_history == null)
{
_history = new List<ProductSaleHistory>();
}
return _history;
}
set
{
if (_history != value)
{
_history = value;
}
}
}
}
public class ProductSaleHistory
{
public ProductSaleHistory()
{
//blank
}
public ProductSaleHistory(int productId, DateTime date, double value)
{
ProductId = productId;
Date = date;
Value = value;
}
public int ProductId { get; set; }
public DateTime Date { get; set; }
public double Value { get; set; }
}
The final step is to wire up the views to our view model. An instance of our view model can be declared in our Application Resources:
<!--Application Resources-->
<Application.Resources>
<ViewModels:MainViewModel x:Key="vm" />
</Application.Resources>
And each view’s DataContext can be set to this instance like this:
<UserControl.DataContext>
<Binding Source="{StaticResource vm}" />
</UserControl.DataContext>
Conclusion
This post demonstrated how to create and bind a few ComponentOne Windows Phone controls using only popular XAML-binding techniques. You can download the full sample below, which also adds some animation to the chart (not mentioned above). The sample is built using the 7.1 OS RC. And be sure to check out Studio for Windows Phone and its other great controls for data apps like Maps and NumericBox! Product Inventory Tracker Sample