Mobile Zone is brought to you in partnership with:

My name is Nico, I’m an MCP living in Belgium. I’m currently employed as a .net developer at RealDolmen, one of Belgium’s leading IT single source providers. In my spare team I'm usually playing around with the Windows Phone and Windows 8 SDK, trying out cool stuff and blogging about it. I'm also founding member and board member of the Belgian Metro App Developer Network, a user group focussed on Windows 8 and Windows Phone and a Dzone MVB Nico is a DZone MVB and is not an employee of DZone and has posted 9 posts at DZone. You can read more from them at their website. View Full User Profile

Porting a Real Win8 App to WP8

04.26.2013
| 2648 views |
  • submit to reddit

It’s been a good while since I last worked on porting Comic Cloud from Windows 8 to Windows Phone. If you can still remember, the goal was to maximize code reuse by using PCL wherever possible.

Part 3 will be the last part in this series, I’m currently holding a fully functional Windows Store app and a Windows Phone 8 app that can navigate pages and sent a search query to the api using a shared service layer. Theoretically everything is shared between the two platforms except the views, which makes sense. But it still required quite a lot of tinkering to get it to work.

PCL is improving

Microsoft is working hard on bringing as many libraries to PCL as they possibly can. In part 2 of the series I already mentioned the portable HttpClient, that library finally gave us a uniform way of doing HTTP requests on multiple platforms. Between part 2 and this part Microsoft has released the PCL version of their Azure Mobile Services SDK (beware! this one has breaking changes if you’re coming over from the platform specific SDK).

Changes in my project

I decided not to use the PCL version of WAMS yet because it has breaking changes and it doesn’t help me get rid of some platform specific projects, so no real use there yet.

What I wanted to achieve for demoing purpose was to get the search functionality working on the phone. The search function on the Windows Store app uses a BlockingCollection (MSDN link) This is a thread safe collection, meaning I can safely prefetch data from one thread while loading data on the other thread. My entire search service is relying on this class (it’s an implementation of the consumer/producer pattern by the way), only problem: Windows Phone doesn’t have the BlockingCollection class. So I could either abstract the search service, change it entirely or implement my own version of the BlockingCollection. The last option seemed like the hardest one to do so I went for it. I’m not entirely sure if I got the exact same functionality of the real BlockingCollection (it does lack some methods and properties, I only implemented what I needed for my app) but here it is

    public class BlockingCollection<T> : Queue<T>
    {
        private readonly object _locker = new object();
        private readonly Queue<T> _itemQ;
        private bool _canAddItems;
     
        public BlockingCollection()
        {
            _itemQ = new Queue<T>();
            _canAddItems = true;
        }
     
        public void EnqueueItem(T item)
        {
            lock (_locker)
            {
                _itemQ.Enqueue(item); // We must pulse because we're
                Monitor.Pulse(_locker); // changing a blocking condition.
            }
        }
     
        public bool TryTake(out T item, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
     
            if (_canAddItems)
            {
                lock (this)
                {
                    try
                    {
                        item = Dequeue();
                        return true;
                    }
                    catch (Exception)
                    {
                        item = default(T);
                        return false;
                    }
                }
            }
     
            item = default(T);
            return false;
        }
     
        public bool TryAdd(T item, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
     
            if (_canAddItems)
            {
                lock (this)
                {
                    try
                    {
                        Enqueue(item);
                        return true;
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                }
            }
     
            return false;
        }
     
        public void CompleteAdding()
        {
            _canAddItems = false;
        }
    }

It’s basically a Queue with some lock statements, it does work for me but I’m not responsible for any accidents that might occur Glimlach

Sharing ViewModels

All my viewmodels are in a PCL library, managed to get that to work in part 1. The ViewModelLocator can’t be made portable since some using statements are different and the WP8 version might need some other classes then the win8 version. I decided to add the Windows Store ViewModelLocator as a link into the Windows Phone 8 project, adding in some pre-processor directives made it work like a charm (I make this sound easy but it did take some time to get it just right).

    using ComicDB.Framework;
    using ComicDB.SDKBroker;
    using ComicDB.View;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Ioc;
    using Microsoft.Practices.ServiceLocation;
     
    #if !WINDOWS_PHONE
    using ComicDB.Framework.WinRT;
    using ComicDB.SDKBroker.WinRT;
    #else
    using ComicDB.Framework.WP8;
    using ComicDB.SDKBroker.WP8;
    #endif
     
    namespace ComicDB.ViewModel
    {
        public class ViewModelLocator
        {
            public ViewModelLocator()
            {
                ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
     
                if (ViewModelBase.IsInDesignModeStatic)
                {
                    // Create design time view services and models
                    //SimpleIoc.Default.Register<IDataService, DesignDataService>();
                }
                else
                {
                    // Create run time view services and models
    #if !WINDOWS_PHONE
                    SimpleIoc.Default.Register<ComicDB.Framework.Interface.INavigationService, ComicDB.Framework.WinRT.NavigationService>();
    #else
                    SimpleIoc.Default.Register<ComicDB.Framework.Interface.INavigationService, ComicDB.Framework.WP8.NavigationService>();
    #endif
                    SimpleIoc.Default.Register<IService, Service>();
                    SimpleIoc.Default.Register<IMessageApi, MessageApi>();
                    SimpleIoc.Default.Register<IFrameworkApi, FrameworkApi>();
                    SimpleIoc.Default.Register<IDispatcher, Dispatcher>();
                    SimpleIoc.Default.Register<INetworkApi, NetworkApi>();
                }
     
                //register views
    #if !WINDOWS_PHONE
                SimpleIoc.Default.Register<IMainPage, MainPage>();
                SimpleIoc.Default.Register<IVolumeDetailPage, VolumeDetailPage>();
                SimpleIoc.Default.Register<ICharacterDetailPage, CharacterDetailPage>();
                SimpleIoc.Default.Register<ICollectionPage, CollectionPage>();
                SimpleIoc.Default.Register<IDetailPage, DetailPage>();
                SimpleIoc.Default.Register<IIssueDetailPage, IssueDetailPage>();
                SimpleIoc.Default.Register<ILocationDetailPage, LocationDetailPage>();
                SimpleIoc.Default.Register<INewsFeedPage, NewsFeedPage>();
                SimpleIoc.Default.Register<IPersonDetailPage, PersonDetailPage>();
                SimpleIoc.Default.Register<IStoryArcDetailPage, StoryArcDetailPage>();
                SimpleIoc.Default.Register<ITeamDetailPage, TeamDetailPage>();
    #endif
                //register viewmodels
                SimpleIoc.Default.Register<MainViewModel>();
                SimpleIoc.Default.Register<VolumeDetailViewModel>(true);
                SimpleIoc.Default.Register<CharacterDetailViewModel>(true);
                SimpleIoc.Default.Register<TeamDetailViewModel>(true);
                SimpleIoc.Default.Register<IssueDetailViewModel>(true);
                SimpleIoc.Default.Register<SearchViewModel>();
                SimpleIoc.Default.Register<DetailViewModel>(true);
                SimpleIoc.Default.Register<StoryArcDetailViewModel>(true);
                SimpleIoc.Default.Register<LocationDetailViewModel>(true);
                SimpleIoc.Default.Register<PersonDetailViewModel>(true);
                SimpleIoc.Default.Register<CollectionViewModel>();
                SimpleIoc.Default.Register<NewsFeedViewModel>(true);
            }
     
            public MainViewModel Main
            {
                get
                {
                    return ServiceLocator.Current.GetInstance<MainViewModel>();
                }
            }
            //... all other VM properties follow here, left out for demo purpose

The pre-processor directives make the class look a bit dirty but it does get the job done.

At this point the WP8 app started and showed me the mainpage, with the mainviewmodel being its datacontext. Now I wanted to add an appbar with a searchbutton, a few problems there:

  • the default appbar is not bindable (solved with Cimbalino)
  • the mainviewmodel doesn’t have a command to navigate to the searchpage since Windows Store uses the Search charm

I decided to take the quick and dirty solution here so I added a normal appbar with a button and a navigation statement in code behind. The SearchPage has SearchViewModel as datacontext. In Windows Store it was normal for the SearchText property to be immediately holding a value since it came from the Search charm, not the case in WP8. Small change to the viewmodel so that it doesn’t fire its Search function when SearchText is empty or null. This was the result after all my hard work

 

Mission accomplished!

Conclusion

PCL still has a long way to go but it is improving, and for some cases it can actually already be very useful (for example to share model classes over different platforms).

I would however advice against going for maximum code reuse, it all sounds great but the reality is very different. I had to make a lot of decisions, change quite a lot of architecture and even add missing classes (like the BlockingCollection).

My advice if you want to build a multiplatform app: use PCL to share your model, maybe even some small framework with helper classes, but build a custom implementation of service layers and viewmodels for each platform, it will save you a lot of hassle and probably even time. If you do decide to go for maximum code reuse, make sure that you really really think about it when you design your architecture, make sure that every little thing has an abstraction better one interface too many than having to rewrite a class.

Here’s a comparing screenshot between the solution before and after adding the WP8 project and refactoring everything.









Published at DZone with permission of Nico Vermeir, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)