Sunday, March 16, 2014

Portable Code Strategies for Windows Store and Windows Phone 8 App Development : Shared Source Code using “Add As Link” and #if conditional blocks

In this post let’s see how we can use Shared Source Code using “Add As Link” and #if conditional blocks to design a common code base for Windows Store and Windows Phone 8 App development.

I am firing up a new instance of Visual Studio and I am creating a new project. The type of project I am going to create is “Windows Phone App”. I am naming the project as “WindowsPhone8App” and for the solution name I am giving "SharedSourceCodeDemo”. Inside the project I am creating three folders named “Model”, “ViewModel” and “Images”. I am adding some images to the “Images” folder. Inside “Model” folder I am adding a class named “PhotoItem”.

PhotoItem.cs
using System;
using System.Collections.Generic;
using System.Windows.Media.Imaging;
 
namespace WindowsPhone8App.Model
{
    public class PhotoItem
    {
        public string PhotoName { get; set; }
        public BitmapImage Photo { get; set; }
 
        public static List<PhotoItem> GetPhotos()
        {
            return new List<PhotoItem>()
            {
                new PhotoItem(){PhotoName="Image1",Photo = new BitmapImage(new Uri("/Images/Image1.jpg", UriKind.Relative))},
                new PhotoItem(){PhotoName="Image2",Photo = new BitmapImage(new Uri("/Images/Image2.jpg", UriKind.Relative))},
            };
        }
    }
}
Inside “ViewModel” folder, I am adding a class named “PhotoItemViewModel”.

PhotoItemViewModel.cs
using WindowsPhone8App.Model;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
 
namespace WindowsPhone8App.ViewModel
{
    public class PhotoItemViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<PhotoItem> photoList;
        public ObservableCollection<PhotoItem> PhotoList
        {
            get
            {
                return photoList;
            }
            set
            {
                photoList = value;
                NotifyPropertyChanged();
            }
        }
 
        public void LoadData()
        {
            PhotoList = new ObservableCollection<PhotoItem>(PhotoItem.GetPhotos());
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
Now inside my MainPage.xaml I have the following design.

MainPage.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <phone:LongListSelector ItemsSource="{Binding PhotoList}">
        <phone:LongListSelector.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding PhotoName}"></TextBlock>
                    <Image Source="{Binding Photo}"></Image>
                </StackPanel>
            </DataTemplate>
        </phone:LongListSelector.ItemTemplate>
    </phone:LongListSelector>
</Grid>
Now inside my MainPage.xaml.cs I have the following code.

MainPage.xaml.cs
using System.Windows;
using Microsoft.Phone.Controls;
using WindowsPhone8App.ViewModel; 

namespace WindowsPhone8App
{
    public partial class MainPage : PhoneApplicationPage
    {
        PhotoItemViewModel viewModel = new PhotoItemViewModel();
 
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += MainPage_Loaded;
        }
 
        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            viewModel.LoadData();
            DataContext = viewModel;
        }
    }
}
Now when I run the app, it is working as expected.

Picture1
Windows Phone 8
Now let’s see how we can use the above two classes in a Windows Store App and that's of course without copy and pasting the files across the projects.

I am adding a new project to the solution of type “Blank App(XAML)” under Windows Store App templates which I am naming as “WindowsStoreApp”. There I am creating three folders named “Model”, “ViewModel” and “Images”. Now I am right clicking on the Model and clicking on Add->Existing Item. I am navigating to the “WindowsPhone8App” projects’ model folder and selecting the “PhotoItem.cs”. Instead of clicking on “Add”, I am selecting “Add As Link.”

Untitled
"Add As Link"
I am doing the same for “PhotoItemViewModel.cs” and for my Images. I am adding all these as “Add As Link” to respective folders.

Now I can see my project files in the Solution Explorer as follows.

Picture1
Files added" using "Add As Link"
Now when I open the “PhotoItem.cs” from my “WindowsStoreApp” project, I am getting some errors.

Untitled
Error
That is of course I can’t use Windows Phone 8 Assemblies inside my Windows Store Application. For that I am going to use #if conditional blocks. I can find the compilation symbol for Windows Store Apps in project properties. Inside Build tab, there it is as "NETFX_CORE". Same way you can find the compilation symbol for Windows Phone 8 apps which is "WINDOWS_PHONE"

Picture1
Conditional compilation symbols
Now I am modifying the "PhotoItem" class as follows.

PhotoItem.cs
using System;
using System.Collections.Generic; 

#if NETFX_CORE
using Windows.UI.Xaml.Media.Imaging;
#endif
 
#if WINDOWS_PHONE
using System.Windows.Media.Imaging;
#endif
 
namespace WindowsPhone8App.Model
{
    public class PhotoItem
    {
        public string PhotoName { get; set; }
 
#if NETFX_CORE
        public string Photo { get; set; }
 
        public static List<PhotoItem> GetPhotos()
        {
            return new List<PhotoItem>()
            {
                new PhotoItem(){PhotoName="Image1",Photo = "/Images/Image1.jpg"},
                new PhotoItem(){PhotoName="Image2",Photo = "/Images/Image2.jpg"},
            };
        }
#endif
 
#if WINDOWS_PHONE
        public BitmapImage Photo { get; set; }
 
        public static List<PhotoItem> GetPhotos()
        {
            return new List<PhotoItem>()
            {
                new PhotoItem(){PhotoName="Image1",Photo = new BitmapImage(new Uri("/Images/Image1.jpg", UriKind.Relative))},
                new PhotoItem(){PhotoName="Image2",Photo = new BitmapImage(new Uri("/Images/Image2.jpg", UriKind.Relative))},
            };
        }
#endif
    }
}
Here I have inserted some #if conditional blocks for using statements as well as inside the class. That’s because in Windows Store App, I can’t directly bind image to a BitmapImage, instead I need to set the Image source Uri as a string. The thing to understand here is there is only one PhotoItem class, I am using #if conditional blocks to state which part I am going to use in Windows Store App and which part I am going to use in Windows Phone 8 App.

Now once this is done, I am modifying the MainPage.xaml inside my Windows Store App to bind to “PhotoItemViewModel”.

MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView ItemsSource="{Binding PhotoList}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding PhotoName}"></TextBlock>
                    <Image Source="{Binding Photo}" Width="100" Height="100"></Image>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>
MainPage.xaml .cs
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using WindowsPhone8App.ViewModel;
 
namespace WindowsStoreApp
{
    public sealed partial class MainPage : Page
    {
        PhotoItemViewModel viewModel = new PhotoItemViewModel();
 
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += MainPage_Loaded;
        }
 
        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            viewModel.LoadData();
            DataContext = viewModel;
        }
    }
}
After completing all these steps, I am running my Windows Store App. I am getting the output as follows.
Picture1
Windows Store App
I am uploading the sample to my SkyDrive, enjoy!


Happy Coding.

Regards,
Jaliya