Friday, March 21, 2014

Portable Code Strategies for Windows Store and Windows Phone 8 App Development : Portable Class Libraries and Platform Abstraction

In this post let’s see how we can use Portable Class Libraries and abstract classes to design a common code base for Windows Store and Windows Phone 8 App development.

Portable Class Library

Portable Class Library project template was initially introduced with Visual Studio 2012 and this is not available in Visual Studio Express Editions.  The Portable Class Library project enables you to write and build portable assemblies that work without modification on multiple platforms, such as Windows 7, Windows 8, Silverlight, Windows Phone, and Xbox 360. You can create classes that contain code you wish to share across many projects, such as shared business logic, and then reference those classes from different types of projects.

Best example would be use of HttpClient to bind a online XML Data source to a ListView in Windows Store App or to a LongListSelector in Windows Phone 8 App. Now let’s see  this example in action.

I have the following XML file hosted in Local IIS.
<company>
  <employee>
    <employeeid>1</employeeid>
    <firstname>Jaliya</firstname>
    <lastname>Udagedara</lastname>
    <age>27</age>
  </employee>
  <employee>
    <employeeid>2</employeeid>
    <firstname>Martin</firstname>
    <lastname>Smith</lastname>
    <age>15</age>
  </employee>
  <employee>
    <employeeid>3</employeeid>
    <firstname>Jane</firstname>
    <lastname>Doe</lastname>
    <age>27</age>
  </employee>
  <employee>
    <employeeid>4</employeeid>
    <firstname>David</firstname>
    <lastname>Smith</lastname>
    <age>22</age>
  </employee>
</company>
I am opening up Visual Studio and creating a project of type “Portable Class Library”. I am naming the project name as “PCLCommon” and the solution name as “PCLAndPlatformAbstractionDemo”.

Portable Class Library
In the next window, I am selecting the following target frameworks.

Target Frameworks
Inside the project, I am adding two folders named “Model” and “ViewModel”. Inside the “Model” folder, I am adding a new class named “Employee”.

Employee.cs
namespace PCLCommon.Model
{
    public class Employee
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
    }
}
Inside the “ViewModel” folder, I am adding a new class named “EmployeeViewModel” which implements INotifyPropertyChanged interface.

EmployeeViewModel,cs
using PCLCommon.Model;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace PCLCommon.ViewModel
{
    public class EmployeeViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<Employee> employees;
        public ObservableCollection<Employee> Employees
        {
            get
            {
                return employees;
            }
            set
            {
                employees = value;
                NotifyPropertyChanged();
            }
        }

        public async Task LoadData()
        {
            HttpClient client = new HttpClient();
            string content = await client.GetStringAsync(new Uri("http://169.254.93.82/datasources/employee.xml"));
            XDocument xDoc = XDocument.Parse(content, LoadOptions.None);
            var query = from e in xDoc.Descendants("employee")
                        select new Employee()
                        {
                            EmployeeId = (int)e.Element("employeeid"),
                            FirstName = (string)e.Element("firstname"),
                            LastName = (string)e.Element("lastname"),
                            Age = (int)e.Element("age")
                        };

            Employees = new ObservableCollection<Employee>();

            foreach (var employee in query)
            {
                Employees.Add(employee);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
Here, I have a ObservableCollection of type “Employee” named “Employees” and I have a method called “LoadData” which reads the locally hosted XML file and inserts “Employee” entities to my “Employees” ObservableCollection.

Now I am adding two projects to the solution, one is a Windows Phone App and the other is a Windows Store App. From both these project I am adding a reference to my “PCLCommon” project.

Picture1
Add Rerefence
Once I do that, I am modifying the MainPage.xaml in both Windows Phone 8 App and Windows Store App.

Windows Phone 8 App – MainPage.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <phone:LongListSelector  ItemsSource="{Binding Employees}">
        <phone:LongListSelector.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding FirstName}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </phone:LongListSelector.ItemTemplate>
    </phone:LongListSelector>
</Grid>

Windows Store App – MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView ItemsSource="{Binding Employees}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding FirstName}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>
Now inside the code behind of MainPage.xaml in both projects, I am writing the following code.

Windows Phone 8 App and Windows Store App – MainPage.xaml.cs
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    EmployeeViewModel viewModel = new EmployeeViewModel();
    await viewModel.LoadData();
    DataContext = viewModel;
}
Now when I run both these projects, I am getting the desired output.

Picture1
Windows Pone 8 App
Picture2
Windows Store App

Abstract Class

Let’s say there is a need that from my Portable Class Library, I need to save some values in Local Settings in both Windows Phone and in the device running Windows Store App. There I am facing a problem, because accessing Local Settings in both these platforms are different from one to another. But I should be able to access the Local Settings in Windows Phone and in the device running Windows Store App in the same manner regardless of the platform. For these kind of scenarios, I can use abstract classes.

To demonstrate the use of abstract classes in writing cross platform applications, I am modifying the above solution further.

In my “PCLCommon” project, I am adding a new abstract class named “SettingManager”.

SettingManager.cs
namespace PCLCommon
{
    public abstract class SettingManager
    {
        public abstract void SetLocalValue(string key, object value);
        public static SettingManager Instance { get; set; }
    }
}
Now in my Windows Phone 8 App and in my Windows Store App, I am adding a class named “SettingHelper” and I am deriving the class from “SettingManager” in “PCLCommon” project.

Windows Phone 8 App – SettingHelper.cs
using PCLCommon;
using System.IO.IsolatedStorage;
 
namespace WindowsPhone8App
{
    public class SettingsHelper:SettingManager
    {
        public override void SetLocalValue(string key, object value)
        {
            IsolatedStorageSettings.ApplicationSettings.Add(key, value);
        }
    }
}
Windows Store App – SettingHelper.cs
using PCLCommon;
using Windows.Storage;
 
namespace WindowsStoreApp
{
    public class SettingsHelper:SettingManager
    {
        public override void SetLocalValue(string key, object value)
        {
            ApplicationData.Current.LocalSettings.Values[key] = value;
        }
    }
}
As you can see, inside the "SettingHelper" class in Windows Phone 8 App and Windows Store App, I have overrided the “SetLocalValue()” method in “SettingManager” with platform specific functionality.

Now inside the code behind of MainPage.xaml in both Windows Phone 8 and Windows Store App projects, I am writing the following code.

Windows Phone 8 App and Windows Store App – MainPage.xaml.cs
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    // set the SettingManager instance to Windows Phone 8/Windows Store App implementation of “SettingManager”
    SettingManager.Instance = new SettingsHelper();
}
Here I am setting up the "SettingManager" instance to Windows Phone 8/Windows Store App implementation of “SettingManager”.

Now from my Portable Class Library, I can write the following code to insert values to Local Settings.
SettingManager.Instance.SetLocalValue("SomeKey", "SomeValue");
When I am running the Windows Phone 8 app, the instance in “SettingManager” would be Windows Phone 8 Apps’ implementation of “SettingManager”. When I am running the Windows Store app, the instance would be Windows Store Apps’ implementation of “SettingManager”. So to the respective Local Settings, data will be inserted.

Appreciate your feedback.

I am uploading the sample to my SkyDrive, enjoy!


Happy Coding.

Regards,
Jaliya