[Telerik] AutoCompleteBox for Universal apps

Scope

This article has the goal to show how to use the AutoCompleteBox for Universal Apps, from Telerik.

Introduction

Some time ago Telerik published the UI For Windows Universal as a Beta version, which can be download in Telerik WebSite.

telerik

For now is only available the following controls:

In this article we will focus in the AutoCompleteBox. Let’s see how we can use it!

Description

Before download and install UI For Windows Universal, we should create a Blank Universal App, then we need to add the reference for this. The following image show how to add it:
add reference

and thensdk extension

Now we can start use the Telerik controls, but before we can move MainWindow.xaml and App.xaml for the shared project for reuse the code.

Note: In this article we will use MVVMLight, see more about here.

In MainWindow we can define the UI  as following

<Page
    x:Class="MyTelerikSamples.AutoCompleteBoxSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:input="using:Telerik.Universal.UI.Xaml.Controls.Input"
    mc:Ignorable="d"
    DataContext="{Binding Main, Source={StaticResource Locator}}"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="{StaticResource HeaderWidth}"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="{StaticResource HeaderHeigth}"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Style="{StaticResource HeaderTextBlockStyle}" Grid.ColumnSpan="2">RadAutoCompleteBox Sample</TextBlock>
        <StackPanel Margin="20,10,0,0" Orientation="Vertical"  Grid.Row="1" Grid.Column="1">
            <TextBlock  FontSize="20">Insert some text:</TextBlock>
            <input:RadAutoCompleteBox Margin="0,20,0,0"
                     FilterMode="Contains" IsTextMatchHighlightEnabled="True"
                     Text="{Binding Value, Mode=TwoWay}" 
                     FilterComparisonMode="CurrentCultureIgnoreCase"
                     ItemsSource="{Binding Suggestions}" AutosuggestFirstItem="False"
                     Width="200" HorizontalAlignment="Left">
                <input:RadAutoCompleteBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"
                               input:RadAutoCompleteBox.IsTextMatchHighlightEnabled="True">
                          <input:RadAutoCompleteBox.TextMatchHighlightStyle>
                              <input:HighlightStyle Foreground="DeepPink" FontSize="21"/>
                          </input:RadAutoCompleteBox.TextMatchHighlightStyle>
                        </TextBlock>
                    </DataTemplate>
                </input:RadAutoCompleteBox.ItemTemplate>
            </input:RadAutoCompleteBox>
        </StackPanel>
    </Grid>
</Page>

In AutoCompleteBox we defined a binding for the Text, which define the text string we will write in the control and we defined the ItemsSource which are the suggestion we will provide to the control, with this each time we start to write a word the control will try to find available words that match with the user input.

The DataTemplate was defined for allow to highlight the matched chars when we insert the text, in this case will be highlight by Pink color.

The MainViewModel can be defined as following

 public class MainViewModel : ViewModelBase
    {
        private string _value;

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>
        /// The value.
        /// </value>
        public string Value
        {
            get { return _value; }
            set { Set(() => Value, ref _value, value); }
        }

        /// <summary>
        /// Gets the suggestions.
        /// </summary>
        /// <value>The suggestions.</value>
        public IEnumerable<string> Suggestions
        {
            get
            {
                return new List<string>
                {
                     "Apple",
                     "Babaco",
                     "Bacupari",
                     "Bacuri",
                     "Black cherry",
                     "Pineapples",
                     "Orange",
                     "Tomato",
                };
            }
        }
    }

After it we can run the app for test if the behavior do what we want, but before we should change the style for the selected item, because it will show the default colors and for the highlight was choosen Pink. For it, create a dictionary for add the resource we want to override

    
    <SolidColorBrush x:Key="TelerikAutoCompleteBoxSelectorSelectedItemBackgroundBrush" Color="DeepPink"/>
    <SolidColorBrush x:Key="TelerikAutoCompleteBoxFocusedForegroundBrush" Color="White"/>

then in App.xaml add the resources and the themes, as following

<Application
    x:Class="MyTelerikSamples.AutoCompleteBoxSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:viewModel="using:MyTelerikSamples.AutoCompleteBoxSample.ViewModel"
    xmlns:controls="using:Telerik.Universal.UI.Xaml.Controls">
    <Application.Resources>
        <ResourceDictionary>
            <viewModel:ViewModelLocator x:Key="Locator"/>
            <controls:UserThemeResources x:Key="ThemeResource" 
                                         DarkResourcesPath="ms-appx:///Styles/TelerikResources.xaml"
                                         LightResourcesPath="ms-appx:///Styles/TelerikResources.xaml"/>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary x:Key="Default">
                    <ResourceDictionary.MergedDictionaries>
                        <!-- your namespace is "Telerik.Universal.UI.Xaml.Input" -->
                        <ResourceDictionary Source="ms-appx:///Telerik.Universal.UI.Xaml.Input/Themes/ThemeResourcesDark.xaml"/>
                        <ResourceDictionary Source="{CustomResource DarkResourcesPath}"/>
                    </ResourceDictionary.MergedDictionaries>
                </ResourceDictionary>
                <ResourceDictionary x:Key="Light">
                    <ResourceDictionary.MergedDictionaries>
                        <ResourceDictionary Source="ms-appx:///Telerik.Universal.UI.Xaml.Input/Themes/ThemeResourcesLight.xaml"/>
                        <ResourceDictionary Source="{CustomResource LightResourcesPath}"/>
                    </ResourceDictionary.MergedDictionaries>
                </ResourceDictionary>
            </ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Style/Definition.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Running the app

When we run the app, we will get

sample

sample

sample

Source Code

The source code is available in github.

See Also

Telerik UI for Windows 8 XAML Documentation – Telerik Named Brushes

Telerik UI for Windows 8 XAML Documentation – Resolving Telerik named resources

Modern UI for WPF application by example ( NavigationService – MVVM )

Scope

This article has the goal to show how to create a navigation service for WPF application that uses Modern UI.

Introduction

Modern UI is a set of controls and styles converting our WPF application into a great looking Modern UI app. The Modern UI project can be find in mui.codeplex.com, here is possible to get the WPF app that demostrate the features provided by “mui”.

WPF don´t have a pattern for the navigation when we are using Windows, only exist a navigation service when we use Pages. ModernUI introduce a special way for the navigation, that use the ModernFrame.

For the sample we will use MVVMLight Toolkit for help in the MVVM pattern implementation and we will use a new feature provided by this toolkit, the INavigationService interface.

Note: The MVVMLight Toolkit don´t have any implementation of INavigationService for WPF, for see more about it read this article  Announcing MVVM Light V5 for Windows and Xamarin.

 Description

The source code base, that we will used in this sample, is similar to the sample used in the article Modern UI for WPF application by example (Default Window).

We will start to create the interface IModernNavigationService and the NavigationService, after it we will configure the navigation in MainWindow and then wil use the navigation service in the view model for navigate to another view.

The interface will be something like

    public interface IModernNavigationService : INavigationService
    {
        /// <summary>
        /// Gets the parameter.
        /// </summary>
        /// <value>
        /// The parameter.
        /// </value>
        object Parameter { get; }
    }

which implementation will be

    public class NavigationService : IModernNavigationService
    {
        private readonly Dictionary<string, Uri> _pagesByKey;
        private readonly List<string> _historic;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="NavigationService"/> class.
        /// </summary>
        public NavigationService()
        {
            _pagesByKey = new Dictionary<string, Uri>();
            _historic = new List<string>();
        }

        /// <summary>
        /// Gets the key corresponding to the currently displayed page.
        /// </summary>
        /// <value>
        /// The current page key.
        /// </value>
        public string CurrentPageKey
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the parameter.
        /// </summary>
        /// <value>
        /// The parameter.
        /// </value>
        public object Parameter { get; private set; }

        /// <summary>
        /// The go back.
        /// </summary>
        public void GoBack()
        {
            if (_historic.Count > 1)
            {
                _historic.RemoveAt(_historic.Count - 1);
                NavigateTo(_historic.Last(), null);
            }
        }

        /// <summary>
        /// The navigate to.
        /// </summary>
        /// <param name="pageKey">
        /// The page key.
        /// </param>
        public void NavigateTo(string pageKey)
        {
            NavigateTo(pageKey, null);
        }

        /// <summary>
        /// The navigate to.
        /// </summary>
        /// <param name="pageKey">
        /// The page key.
        /// </param>
        /// <param name="parameter">
        /// The parameter.
        /// </param>
        public virtual void NavigateTo(string pageKey, object parameter)
        {
            lock (_pagesByKey)
            {
                if (!_pagesByKey.ContainsKey(pageKey))
                {
                    throw new ArgumentException(string.Format("No such page: {0}. Did you forget to call NavigationService.Configure?", pageKey), "pageKey");
                }

                var frame = GetDescendantFromName(Application.Current.MainWindow, "ContentFrame") as ModernFrame;

                // Set the frame source, which initiates navigation
                if (frame != null)
                {
                    frame.Source = _pagesByKey[pageKey];
                }
                Parameter = parameter;
                _historic.Add(pageKey);
                CurrentPageKey = pageKey;
            }
        }

        /// <summary>
        /// Configures the specified key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="pageType">Type of the page.</param>
        public void Configure(string key, Uri pageType)
        {
            lock (_pagesByKey)
            {
                if (_pagesByKey.ContainsKey(key))
                {
                    _pagesByKey[key] = pageType;
                }
                else
                {
                    _pagesByKey.Add(key, pageType);
                }
            }
        }

        /// <summary>
        /// Gets the name of the descendant from.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="name">The name.</param>
        /// <returns>The FrameworkElement.</returns>
        private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
        {
            var count = VisualTreeHelper.GetChildrenCount(parent);

            if (count < 1)
            {
                return null;
            }

            for (var i = 0; i < count; i++)
            {
                var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
                if (frameworkElement != null)
                {
                    if (frameworkElement.Name == name)
                    {
                        return frameworkElement;
                    }

                    frameworkElement = GetDescendantFromName(frameworkElement, name);
                    if (frameworkElement != null)
                    {
                        return frameworkElement;
                    }
                }
            }

            return null;
        }
    }

and the setup for the navigation will be done in the MainWindow.xaml.cs, something like

        private void SetupNavigation()
        {
            var navigationService = new NavigationService();
            navigationService.Configure(ViewModelLocator.ResourcePageKey, new Uri("Views/ResourcesView.xaml"));
            navigationService.Configure(ViewModelLocator.StepsPageKey, new Uri("Views/StepsView.xaml"));

            SimpleIoc.Default.Register<IModernNavigationService>(() => navigationService);
        }

 

In the StepsViewModel we will do

 

    public class StepsViewModel : ViewModelBase
    {
        private readonly IModernNavigationService _modernNavigationService;

        /// <summary>
        /// Initializes a new instance of the <see cref="StepsViewModel"/> class. 
        /// </summary>
        /// <param name="modernNavigationService">
        /// The modern Navigation Service.
        /// </param>
        public StepsViewModel(IModernNavigationService modernNavigationService)
        {
            _modernNavigationService = modernNavigationService;
            ResourcesCommand = new RelayCommand(ShowResources);
        }

        /// <summary>
        /// Gets or sets the resources command.
        /// </summary>
        /// <value>The resources command.</value>
        public ICommand ResourcesCommand { get; set; }

        /// <summary>
        /// Shows the resources.
        /// </summary>
        private void ShowResources()
        {
            _modernNavigationService.NavigateTo(ViewModelLocator.ResourcePageKey);
        }
    }

 

Note:

1. For send a parameter when navigate we should do

 _modernNavigationService.NavigateTo(ViewModelLocator.ResourcePageKey, myParameterValue);

and then in the view model use the Parameter property, from the navigation service, for get the parameter

     _modernNavigationService.Parameter

2. For navigate to the preview page use the GoBack method

 _modernNavigationService.GoBack();

Source code

Get the source code for this sample in github.

 

Visual Studio extension used

resharper    ghostdoc  stylecop

Modern UI for WPF application by example ( NavigationMessageService – MVVM )

Scope

This article has the goal to show how to create a navigation mensage service for WPF application that uses Modern UI.

Introduction

Modern UI is a set of controls and styles converting our WPF application into a great looking Modern UI app. The Modern UI project can be find in mui.codeplex.com, here is possible to get the WPF app that demostrate the features provided by “mui”.

WPF don´t have a pattern for the navigation when we are using Windows, only exist a navigation service for Pages. ModernUI introduce a special way for the navigation, that use the ModernFrame.

In the article  Modern UI for WPF application by example (Handle Navigation: (Default)) we saw how to handle the navigation. In this sample we will see how to navigate between views using messages.

Description

The navigation we will implement is based in messages send from a view model to the MainWindow, these messages are send/received by Messenger class provided by MVVMLight Toolkit.

Start by create the NavigationMessage as following

    public class NavigationMessage
    {
        /// <summary>
        /// Gets or sets the page.
        /// </summary>
        /// <value>The page.</value>
        public string Page { get; set; }
    }

In the MainWindow register for receive the message defined as following

        private void RegisterNavigation()
        {
            Messenger.Default.Register<NavigationMessage>(this, p =>
            {
                var frame = GetDescendantFromName(this, "ContentFrame") as ModernFrame;
                 
                // Set the frame source, which initiates navigation
                if (frame != null)
                {
                    frame.Source = new Uri(p.Page, UriKind.Relative);
                }
            });
        }

Where GetDescendantFromName is the method that use VisualTreeHelper for get the ModernFrame.

In the StepsViewModel send the NavigationMessage with the path for the view we wants to navigate

        private void ShowResources()
        {
            Messenger.Default.Send(new NavigationMessage()
            {
                Page = "Views/ResourcesControl.xaml"
            }); 
        }

 

Where we only navigated to the new view, for send parameters when navigate we should use another message with the parameter we want to send, with it the comunication will we between view models.

 

Source code

Get the source code for this sample in github.

 

Visual Studio extension used

resharper    ghostdoc  stylecop

Modern UI for WPF application by example (Handle Navigation)

Scope

This article has the goal to show how to create a blank window in WPF using Modern UI, which handle the navigation.

Introduction

Modern UI is a set of controls and styles converting our WPF application into a great looking Modern UI app. The Modern UI project can be find in mui.codeplex.com, here is possible to get the WPF app that demostrate the features provided by “mui”.

WPF don´t have a pattern for the navigation when we are using Windows, only exist a navigation service when we use Pages. ModernUI introduce a special way for the navigation, that use the ModernFrame.

The question here is, how I know when the user is navigating from one view to another?

 

Description

In the sample Modern UI for WPF application by example (Default Window) we saw the default window provided by Modern UI, and in this sample we will use the same base code but we will change the code for handle the navigation.

If we search the properties from the  ModernWindow, we will see that it don´t have any event or method for handle navigation. After some search in documentation, we find an article Handle navigation events in your content.

Let’s apply it!

The MainWindow contains a MenuLinkGroups which contains a LinkGroup with two Links. Each link is defined by a UserControl. For handle navigation, each user control will implement the interface IContent, like following

    /// <summary>
    /// Interaction logic for StepsControl.xaml.
    /// </summary>
    public partial class StepsControl : IContent
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="StepsControl"/> class.
        /// </summary>
        public StepsControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Called when navigation to a content fragment begins.
        /// </summary>
        /// <param name="e">An object that contains the navigation data.</param>
        public void OnFragmentNavigation(FragmentNavigationEventArgs e)
        {
            Debug.WriteLine("StepsControl- OnFragmentNavigation");
        }

        /// <summary>
        /// Called when this instance is no longer the active content in a frame.
        /// </summary>
        /// <param name="e">An object that contains the navigation data.</param>
        public void OnNavigatedFrom(NavigationEventArgs e)
        {
            Debug.WriteLine("StepsControl -OnNavigatedFrom");
        }

        /// <summary>
        /// Called when a this instance becomes the active content in a frame.
        /// </summary>
        /// <param name="e">An object that contains the navigation data.</param>
        public void OnNavigatedTo(NavigationEventArgs e)
        {
            Debug.WriteLine("StepsControl- OnNavigatedTo");
        }

        /// <summary>
        /// Called just before this instance is no longer the active content in a frame.
        /// </summary>
        /// <param name="e">
        /// An object that contains the navigation data.
        /// </param>
        /// <remarks>
        /// The method is also invoked when parent frames are about to navigate.
        /// </remarks>
        public void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            Debug.WriteLine("StepsControl- OnNavigatingFrom");
        }
    }

Using Debug.WriteLine we will show in output window the flow when we navigate in MainWindow.

The output will be something like

StepsControl - OnNavigatedTo
StepsControl - OnNavigatingFrom
StepsControl - OnNavigatedFrom
ResourcesControl - OnNavigatedTo
ResourcesControl - OnNavigatingFrom
ResourcesControl - OnNavigatedFrom
StepsControl - OnNavigatedTo
StepsControl - OnNavigatingFrom
StepsControl - OnNavigatedFrom
ResourcesControl - OnNavigatedTo

See the code source in github – ModernUIForWPFSample.Navigation (Default) .

Suppose when we navigate we want to do some task and we are using the  MVVM pattern. We could get the view model defined in DataContext and then call method that apply what we want, something like

        public void OnNavigatedTo(NavigationEventArgs e)
        {
            var viewModel = DataContext as MyViewModel;
            if (viewModel != null)
            {
                viewModel.DoSomething();
            }
        }

 

or we can define events that will be called when the methods from IContent are raised and this events can be defined in UI using the EventTrigger and InvokeCommandAction. With this we will avoid code in code behiden.

First we will create our ModernUserControl which is a UserControl and implements the IContent. This control will have four events, one for each method required for IContent.

The implementation will be something like

    public class ModernUserControl : UserControl, IContent
    {
        /// <summary>
        /// Handles the <see cref="E:FragmentNavigation"/> event.
        /// </summary>
        /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.FragmentNavigationEventArgs"/> instance containing the event data.</param>
        public void OnFragmentNavigation(FragmentNavigationEventArgs e)
        {
            if (FragmentNavigation != null)
            {
                FragmentNavigation(this, e);
            }
        }

        /// <summary>
        /// Handles the <see cref="E:NavigatedFrom"/> event.
        /// </summary>
        /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
        public void OnNavigatedFrom(NavigationEventArgs e)
        {
            if (NavigatedFrom != null)
            {
                NavigatedFrom(this, e);
            }
        }

        /// <summary>
        /// Handles the <see cref="E:NavigatedTo"/> event.
        /// </summary>
        /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
        public void OnNavigatedTo(NavigationEventArgs e)
        {
            if (NavigatedTo != null)
            {
                NavigatedTo(this, e);
			}
        }

        /// <summary>
        /// Handles the <see cref="E:NavigatingFrom"/> event.
        /// </summary>
        /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs"/> instance containing the event data.</param>
        public void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            if (NavigatingFrom != null)
            {
                NavigatingFrom(this, e);
            }
        }

        /// <summary>
        /// Occurs when [navigating from].
        /// </summary>
        public event NavigatingCancelHandler NavigatingFrom;

        /// <summary>
        /// Occurs when [navigated from].
        /// </summary>
        public event NavigationEventHandler NavigatedFrom;

        /// <summary>
        /// Occurs when [navigated to].
        /// </summary>
        public event NavigationEventHandler NavigatedTo;

        /// <summary>
        /// Occurs when [fragment navigation].
        /// </summary>
        public event FragmentNavigationHandler FragmentNavigation;
    }

 

After it, StepsUserControl and ResourcesUserControl will be a ModernUserControl has following

<controls:ModernUserControl x:Class="ModernUIForWPFSample.Navigation.Views.StepsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:controls="clr-namespace:ModernUIForWPFSample.Navigation__MVVM_.Controls"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             d:DesignHeight="300"
             d:DesignWidth="600" 
             mc:Ignorable="d" 
             DataContext="{Binding StepsViewModel, Source={StaticResource Locator}}">
    <i:Interaction.Triggers>
         <i:EventTrigger EventName="NavigatedTo">
            <i:InvokeCommandAction Command="{Binding NavigatedToCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="NavigatedFrom">
            <i:InvokeCommandAction Command="{Binding NavigatedFromCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="NavigatingFrom">
            <i:InvokeCommandAction Command="{Binding NavigatingFromCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="FragmentNavigation">
            <i:InvokeCommandAction Command="{Binding FragmentNavigationCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="IsVisibleChanged">
            <i:InvokeCommandAction Command="{Binding IsVisibleChangedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap">
            Steps:
            <LineBreak />
            1st Install the ModernUI from Nuget
            <LineBreak />
            2nd Define in App.xaml the resources for ModernUI.xaml and ModernUI.Light.xaml
            <LineBreak />
            3rd Change the MainWindow: Replace the tag &quot;Window&quot; to ModernWindow
            <LineBreak />
            4th Define the content for the MainWindow using TitleLinks, MenuLinkGroups, LinkGroup...
            <LineBreak />
            5th Define which content is shown when the application start, by using the ContentSource
            <LineBreak />
            6th For each content (in this case for StepsControl and ResourcesControl) must implement the interface IContent 
            <LineBreak />
            7th For each navigation method (OnFragmentNavigation, OnNavigatedFrom, OnNavigatedTo and OnNavigatingFrom) add the behavior you want
            <LineBreak />
            <LineBreak />
            Note: For change the appearance use  AppearanceManager class
        </TextBlock>
    </Grid>
</controls:ModernUserControl>

 

For help in th MVVM pattern implementation we will use MVVMLight Toolkit and we will have two view models (StepsViewModel and ResourcesViewModel). Here are the class diagram.

ViewModelLocator will help in binding the view model to the view and is where we setup the dependencies when we use dependency injection.

When we ran the application the output will be something like

ModernUserControl - OnNavigatedTo
StepsViewModel - LoadData
ModernUserControl - OnNavigatingFrom
StepsViewModel - NavigatingFrom
ModernUserControl - OnNavigatingFrom event called
ModernUserControl - OnNavigatedFrom
StepsViewModel - NavigatedFrom
ModernUserControl - OnNavigatedFrom event called
ModernUserControl - OnNavigatedTo
StepsViewModel - LoadData
ResourcesViewModel - LoadData
ModernUserControl - OnNavigatingFrom
ResourcesViewModel - NavigatingFrom
ModernUserControl - OnNavigatingFrom event called
ModernUserControl - OnNavigatedFrom
ResourcesViewModel - NavigatedFrom
ModernUserControl - OnNavigatedFrom event called
ModernUserControl - OnNavigatedTo
StepsViewModel - NavigatedTo
ModernUserControl - OnNavigatedTo event called
ResourcesViewModel - LoadData
StepsViewModel - LoadData

With it, we can conclude when we use events, the NavigateTo event is not raised at the first time the control is shown, it because the NavigateTo event wasn´t subscrived when the first navigation occurs.

See others solutions for Navigation in ModernUI applications.

Modern UI for WPF application by example ( NavigationMessageService – MVVM )

Modern UI for WPF application by example ( NavigationService – MVVM )

Source code

Get the source code for this sample in github.

 

Visual Studio extension used

resharper    ghostdoc      stylecop

How to send data through bluetooth in a WPF application using 32feet.Net

Scope

This article has the goal to show a solution for send data between devices using a WPF application and the library 32feet.Net.

The solution will implement the MVVM pattern and will use the MVVMLight Toolkit.

Introduction

Bluetooth is an industry-standard protocol that enables wireless connectivity for computers, handheld devices, mobile phones, and other devices.

Bluetooth is designed for use by C/C++ programmers. Some Bluetooth features are available with Windows Sockets. Familiarity with Microsoft Windows networking and Windows Sockets programming is required. The article Bluetooth Programming with Windows Sockets, describes how to use Windows Sockets functions and structures to program a Bluetooth application and provide the Bluetooth connection sample. But in a first attempt it not looks so nice, when our goal is to create a WPF application.

In Codeplex, there is a project called 32feet.NET. This project is a shared-source project to make personal area networking technologies such as Bluetooth, Infrared (IrDA) and more, easily accessible from .NET code. Supports desktop, mobile or embedded systems.

32feet.NET is available on Nuget, and for desktop apps the reference is this 32feet.NET 3.5.0 Nuget Package. Which version we will use in the sample we will create.

Description

The WPF Application will have two “modes”: Sender and Receiver. Where the “Sender” has the responsibility to send messages and the “Receiver” will get the messages sent by “Sender”.

Let’s start!

Creating the project

First create the WPF application in Visual Studio

 After it, install the nugget packages: MVVMLight and 32feet.Net, as following

Installing MVVM Light Toolkit

Installing 32feet.Net

At the end our solution will have the base structure for implement MVVM, that MVVM Light installed through the nuget.

Now we need to define the model for the device that is required when we get the list of the devices around us with bluetooth on.

The Model

The model is defined by the Device class that represent the structure for the device around us. The implementation is

 public sealed class Device
    {
        /// <summary>
        /// Gets or sets the device name.
        /// </summary>
        /// <value>
        /// The device name.
        /// </value>
        public string DeviceName { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether authenticated.
        /// </summary>
        /// <value>
        /// The authenticated.
        /// </value>
        public bool IsAuthenticated { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether is connected.
        /// </summary>
        /// <value>
        /// The is connected.
        /// </value>
        public bool IsConnected { get; set; }

        /// <summary>
        /// Gets or sets the nap.
        /// </summary>
        /// <value>
        /// The nap.
        /// </value>
        public ushort Nap { get; set; }

        /// <summary>
        /// Gets or sets the sap.
        /// </summary>
        /// <value>
        /// The sap.
        /// </value>
        public uint Sap { get; set; }

        /// <summary>
        /// Gets or sets the last seen.
        /// </summary>
        /// <value>
        /// The last seen.
        /// </value>
        public DateTime LastSeen { get; set; }

        /// <summary>
        /// Gets or sets the last used.
        /// </summary>
        /// <value>
        /// The last used.
        /// </value>
        public DateTime LastUsed { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether remembered.
        /// </summary>
        /// <value>
        /// The remembered.
        /// </value>
        public bool Remembered { get; set; }
        
        /// <summary>
        /// Gets or sets the device info.
        /// </summary>
        /// <value>
        /// The device info.
        /// </value>
        public BluetoothDeviceInfo DeviceInfo { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Device"/> class.
        /// </summary>
        /// <param name="device_info">
        /// The device_info.
        /// </param>
        public Device(BluetoothDeviceInfo device_info)
        {
            if (device_info != null)
            {
                DeviceInfo = device_info;
                IsAuthenticated = device_info.Authenticated;
                IsConnected = device_info.Connected;
                DeviceName = device_info.DeviceName;
                LastSeen = device_info.LastSeen;
                LastUsed = device_info.LastUsed;
                Nap = device_info.DeviceAddress.Nap;
                Sap = device_info.DeviceAddress.Sap;
                Remembered = device_info.Remembered;
            }
        }

        /// <summary>
        /// The to string.
        /// </summary>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public override string ToString()
        {
            return DeviceName;
        }
    }

In a class diagram we will have

The Services

The services in the application will define the features for the “Sender” and for the “Receiver”. These classes will be injected to the view model using the ServiceLocator, and the setup is defined in the ViewModelLocator constructor.

For “Sender” and “Receiver” are connected each other, we need to define a Guid that is set to the ServiceClassId and it is the key for the communication. When “Sender” send a message using the ServiceClassID X only the “Receiver” that know the ServiceClassID X will get the data, any other “Receiver” that know only the ServiceClassID Y, for exemple, will not receive the data.

 The ReceiverBluetoothService

The ReceiverBluetoothService define how the “Receiver” will receive the data from the “Sender”. For it is used a thread that will run and will be listening by data.

The implementation for this class is something like

 

   public class ReceiverBluetoothService : ObservableObject, IDisposable, IReceiverBluetoothService
    {
        private readonly Guid _serviceClassId;
        private Action<string> _responseAction;
        private BluetoothListener _listener;
        private CancellationTokenSource _cancelSource;
        private bool _wasStarted;
        private string _status;

        /// <summary>
        /// Initializes a new instance of the <see cref="ReceiverBluetoothService" /> class.
        /// </summary>
        public ReceiverBluetoothService()
        {
           _serviceClassId = new Guid("0e6114d0-8a2e-477a-8502-298d1ff4b4ba");
        }

        /// <summary>
        /// Gets or sets a value indicating whether was started.
        /// </summary>
        /// <value>
        /// The was started.
        /// </value>
        public bool WasStarted
        {
            get { return _wasStarted; }
            set { Set(() => WasStarted, ref _wasStarted, value); }
        }
        
        /// <summary>
        /// Starts the listening from Senders.
        /// </summary>
        /// <param name="reportAction">
        /// The report Action.
        /// </param>
        public void Start(Action<string> reportAction)
        {
            WasStarted = true;
            _responseAction = reportAction;
            if (_cancelSource != null && _listener != null)
            {
                Dispose(true);
            }
            _listener = new BluetoothListener(_serviceClassId)
            {
                ServiceName = "MyService"
            };
            _listener.Start();

            _cancelSource = new CancellationTokenSource();

            Task.Run(() => Listener(_cancelSource));
        }

        /// <summary>
        /// Stops the listening from Senders.
        /// </summary>
        public void Stop()
        {
            WasStarted = false;
            _cancelSource.Cancel();
        }

        /// <summary>
        /// Listeners the accept bluetooth client.
        /// </summary>
        /// <param name="token">
        /// The token.
        /// </param>
        private void Listener(CancellationTokenSource token)
        {
            try
            {
                while (true)
                {
                    using (var client = _listener.AcceptBluetoothClient())
                    {
                        if (token.IsCancellationRequested)
                        {
                            return;
                        }

                        using (var streamReader = new StreamReader(client.GetStream()))
                        {
                            try
                            {
                                var content = streamReader.ReadToEnd();
                                if (!string.IsNullOrEmpty(content))
                                {
                                    _responseAction(content);
                                }
                            }
                            catch (IOException)
                            {
                                client.Close();
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
               // todo handle the exception
               // for the sample it will be ignored
            }
        }

        /// <summary>
        /// The dispose.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// The dispose.
        /// </summary>
        /// <param name="disposing">
        /// The disposing.
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_cancelSource != null)
                {
                    _listener.Stop();
                    _listener = null;
                    _cancelSource.Dispose();
                    _cancelSource = null;
                }
            }
        }
    }

In the Start method we need to define an action that will be used for report the data received in ViewModel. We could use an event or the Message feature from MVVMLight.

In the Listener method that is running in another thread, we defined a CancellationTokenSource that will be used for stop the process for listening for data.

Note: The “Receiver” allow to start or stop the process for listening data. If a “Sender” send data but the “Receiver” not allow listening, the “Sender” will send the data but “Receiver” will not get it.

The SenderBluetoothService

The SenderBluetoothService define how the “Sender” will send data, but for it is required to select a device that is available. Is not possible to filter for devices that knows the ServiceClassId and the name of the device for where the “Sender” wants to send the data should be known.

The implementation for this class is something like

  public sealed class SenderBluetoothService : ISenderBluetoothService
    {
         private readonly Guid _serviceClassId;

        /// <summary>
        /// Initializes a new instance of the <see cref="SenderBluetoothService"/> class. 
        /// </summary>
        public SenderBluetoothService()
        {
            // this guid is random, only need to match in Sender & Receiver
            // this is like a "key" for the connection!
            _serviceClassId = new Guid("0e6114d0-8a2e-477a-8502-298d1ff4b4ba");
        }

        /// <summary>
        /// Gets the devices.
        /// </summary>
        /// <returns>The list of the devices.</returns>
        public async Task<IList<Device>> GetDevices()
        {
            // for not block the UI it will run in a different threat
            var task = Task.Run(() =>
            {
                var devices = new List<Device>();
                using (var bluetoothClient = new BluetoothClient())
                {
                    var array = bluetoothClient.DiscoverDevices();
                    var count = array.Length;
                    for (var i = 0; i < count; i++)
                    {
                        devices.Add(new Device(array[i]));
                    }
                }
                return devices;
            });
            return await task;
        }

        /// <summary>
        /// Sends the data to the Receiver.
        /// </summary>
        /// <param name="device">The device.</param>
        /// <param name="content">The content.</param>
        /// <returns>If was sent or not.</returns>
        public async Task<bool> Send(Device device, string content)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            if (string.IsNullOrEmpty(content))
            {
                throw new ArgumentNullException("content");
            }

            // for not block the UI it will run in a different threat
            var task = Task.Run(() =>
            {
                using (var bluetoothClient = new BluetoothClient())
                {
                    try
                    {
                        var ep = new BluetoothEndPoint(device.DeviceInfo.DeviceAddress, _serviceClassId);
                       
                        // connecting
                        bluetoothClient.Connect(ep);

                        // get stream for send the data
                        var bluetoothStream = bluetoothClient.GetStream();

                        // if all is ok to send
                        if (bluetoothClient.Connected && bluetoothStream != null)
                        {
                            // write the data in the stream
                            var buffer = System.Text.Encoding.UTF8.GetBytes(content);
                            bluetoothStream.Write(buffer, 0, buffer.Length);
                            bluetoothStream.Flush();
                            bluetoothStream.Close();
                            return true;
                        }
                        return false;
                    }
                    catch
                    {
                        // the error will be ignored and the send data will report as not sent
                        // for understood the type of the error, handle the exception
                    }
                }
                return false;
            });
            return await task;
        }
    }

In the Send method, when we are connected to the selected device, we will be available to write in a stream which is received by “Receiver”.

The ViewModel

We will define the following view models: ReceiverViewModel, SenderViewModel and MainViewModel that will be binding to the DataContext in ReceiverView, SenderView and MainWindow respectively.

The ReceiverViewModel

The implementation will be something like

    public sealed class ReceiverViewModel : ViewModelBase
    {
        private readonly IReceiverBluetoothService _receiverBluetoothService;
        private string _data;
        private bool _isStarEnabled;
        private string _status;

        /// <summary>
        /// Initializes a new instance of the <see cref="ReceiverViewModel" /> class.
        /// </summary>
        /// <param name="receiverBluetoothService">The Receiver bluetooth service.</param>
        public ReceiverViewModel(IReceiverBluetoothService receiverBluetoothService)
        {
            _receiverBluetoothService = receiverBluetoothService;
            _receiverBluetoothService.PropertyChanged += ReceiverBluetoothService_PropertyChanged;
            IsStarEnabled = true;
            Data = "N/D";
            Status = "N/D";
            StartCommand = new RelayCommand(() =>
            {
                _receiverBluetoothService.Start(SetData);
                IsStarEnabled = false;
                Data = "Can receive data.";
            });

            StopCommand = new RelayCommand(() =>
            {
                _receiverBluetoothService.Stop();
                IsStarEnabled = true;
                Data = "Cannot receive data.";
            });

            Messenger.Default.Register<Message>(this, ResetAll);
        }

        /// <summary>
        /// Resets all.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ResetAll(Message message)
        {
            if (!message.IsToShowDevices)
            {
                if (_receiverBluetoothService.WasStarted)
                {
                    _receiverBluetoothService.Stop();
                }
                IsStarEnabled = true;
                Data = "N/D";
                Status = "N/D";
            }
        }

        /// <summary>
        /// The set data received.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        public void SetData(string data)
        {
            Data = data;
        }

        /// <summary>
        /// Gets or sets the data.
        /// </summary>
        /// <value>
        /// The data received.
        /// </value>
        public string Data
        {
            get { return _data; }
            set { Set(() => Data, ref _data, value); }
        }

        /// <summary>
        /// Gets the start command.
        /// </summary>
        /// <value>
        /// The start command.
        /// </value>
        public ICommand StartCommand { get; private set; }

        /// <summary>
        /// Gets the stop command.
        /// </summary>
        /// <value>
        /// The stop command.
        /// </value>
        public ICommand StopCommand { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether is star enabled.
        /// </summary>
        /// <value>
        /// The is star enabled.
        /// </value>
        public bool IsStarEnabled
        {
            get
            {
                return _isStarEnabled;
            }
            set
            {
                Set(() => IsStarEnabled, ref _isStarEnabled, value);
                RaisePropertyChanged(() => IsStopEnabled);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether is stop enabled.
        /// </summary>
        /// <value>
        /// The is stop enabled.
        /// </value>
        public bool IsStopEnabled
        {
            get
            {
                return !_isStarEnabled;
            }
            set
            {
                Set(() => IsStopEnabled, ref _isStarEnabled, !value);
                RaisePropertyChanged(() => IsStarEnabled);
            }
        }

        /// <summary>
        /// Gets or sets the status.
        /// </summary>
        /// <value>The status.</value>
        public string Status
        {
            get { return _status; }
            set { Set(() => Status, ref _status, value); }
        }

        /// <summary>
        /// Handles the PropertyChanged event of the ReceiverBluetoothService control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void ReceiverBluetoothService_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "WasStarted")
            {
                IsStarEnabled = true;
            }
        }
    }

The SenderViewModel

The implementation will be something like

    public sealed class SenderViewModel : ViewModelBase
    {
        private readonly ISenderBluetoothService _senderBluetoothService;
        private string _data;
        private Device _selectDevice;
        private string _resultValue;

        /// <summary>
        /// Initializes a new instance of the <see cref="SenderViewModel"/> class.
        /// </summary>
        /// <param name="senderBluetoothService">
        /// The Sender bluetooth service.
        /// </param>
        public SenderViewModel(ISenderBluetoothService senderBluetoothService)
        {
            _senderBluetoothService = senderBluetoothService;
            ResultValue = "N/D";
            SendCommand = new RelayCommand(
                SendData,
                () => !string.IsNullOrEmpty(Data) && SelectDevice != null && SelectDevice.DeviceInfo != null);
            Devices = new ObservableCollection<Device>
            {
                new Device(null) { DeviceName = "Searching..." }
            };
            Messenger.Default.Register<Message>(this, ShowDevice);
        }

        /// <summary>
        /// Gets or sets the devices.
        /// </summary>
        /// <value>
        /// The devices.
        /// </value>
        public ObservableCollection<Device> Devices
        {
            get; set;
        }

        /// <summary>
        /// Gets or sets the select device.
        /// </summary>
        /// <value>
        /// The select device.
        /// </value>
        public Device SelectDevice
        {
            get { return _selectDevice; }
            set { Set(() => SelectDevice, ref _selectDevice, value); }
        }

        /// <summary>
        /// Gets or sets the data.
        /// </summary>
        /// <value>
        /// The data.
        /// </value>
        public string Data
        {
            get { return _data; }
            set { Set(() => Data, ref _data, value); }
        }

        /// <summary>
        /// Gets or sets the result value.
        /// </summary>
        /// <value>
        /// The result value.
        /// </value>
        public string ResultValue
        {
            get { return _resultValue; }
            set { Set(() => ResultValue, ref _resultValue, value); }
        }

        /// <summary>
        /// Gets the send command.
        /// </summary>
        /// <value>
        /// The send command.
        /// </value>
        public ICommand SendCommand { get; private set; }

        private async void SendData()
        {
            ResultValue = "N/D";
            var wasSent = await _senderBluetoothService.Send(SelectDevice, Data);
            if (wasSent)
            {
                ResultValue = "The data was sent.";
            }
            else
            {
                ResultValue = "The data was not sent.";
            }
        }

        /// <summary>
        /// Shows the device.
        /// </summary>
        /// <param name="deviceMessage">The device message.</param>
        private async void ShowDevice(Message deviceMessage)
        {
            if (deviceMessage.IsToShowDevices)
            {
                var items = await _senderBluetoothService.GetDevices();
                Devices.Clear();
                Devices.Add(items);
                Data = string.Empty;
            }
        }
    }

The MainViewModel

The implementation will be something like

public sealed class MainViewModel : ViewModelBase
    {
        private bool _isReceiver;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewModel"/> class.
        /// </summary>
        public MainViewModel()
        {
            PropertyChanged += MainViewModelPropertyChanged;
            IsSender = false;
        }

        /// <summary>
        /// Gets or sets a value indicating whether is Receiver.
        /// </summary>
        /// <value>
        /// The is Receiver.
        /// </value>
        public bool IsReceiver
        {
            get
            {
                return _isReceiver;
            }
            set
            {
                Set(() => IsReceiver, ref _isReceiver, value);
                RaisePropertyChanged(() => IsSender);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether is Sender.
        /// </summary>
        /// <value>
        /// The is Sender.
        /// </value>
        public bool IsSender
        {
            get
            {
                return !_isReceiver;
            }
            set
            {
                Set(() => IsSender, ref _isReceiver, !value);
                RaisePropertyChanged(() => IsReceiver);
            }
        }

        /// <summary>
        /// Gets or sets the Receiver visibility.
        /// </summary>
        /// <value>
        /// The Receiver visibility.
        /// </value>
        public Visibility ReceiverVisibility
        {
            get
            {
                return _isReceiver ? Visibility.Visible : Visibility.Collapsed;
            }
            set
            {
                _isReceiver = value == Visibility.Visible;
            }
        }

        /// <summary>
        /// Gets or sets the Sender visibility.
        /// </summary>
        /// <value>
        /// The Sender visibility.
        /// </value>
        public Visibility SenderVisibility
        {
            get
            {
                return !_isReceiver ? Visibility.Visible : Visibility.Collapsed;
            }
            set 
            {
                _isReceiver = value != Visibility.Visible;
            }
        }

        /// <summary>
        /// Mains the view model property changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void MainViewModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsReceiver" || e.PropertyName == "IsSender")
            {
                RaisePropertyChanged(() => ReceiverVisibility);            
                RaisePropertyChanged(() => SenderVisibility);

                if (e.PropertyName == "IsReceiver")
                {
                    Messenger.Default.Send(IsSender ? new Message(true) : new Message(false));
                }
            }
        }
    }

 

The UI

The MainWindow will be the start point for the application and will contains two user controls: ReceiverView and SenderView that will be showed if the user want to be a “Sender” or a “Receiver”.

The ReceiverView.xaml

The implementation will be something like

<UserControl x:Class="BluetoothSample.Views.ReceiverView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             DataContext="{Binding ReceiverViewModel, Source={StaticResource Locator}}"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="400">
    <StackPanel Margin="20"  Orientation="Vertical">
        <TextBlock>I am the Receiver</TextBlock>
        <StackPanel Orientation="Horizontal">
            <Button Margin="0,10,0,0" Width="80"  Command="{Binding StartCommand}" IsEnabled="{Binding IsStarEnabled}" Content="Start"/>
            <Button Margin="20,10,0,0" Width="80" Command="{Binding StopCommand}" IsEnabled="{Binding IsStopEnabled}" Content="Stop"/>
        </StackPanel>
        <TextBlock Margin="00,20,0,0" Text="Data:"/>
        <TextBlock Margin="00,20,0,0" Text="{Binding Data}"/>
    </StackPanel>
</UserControl>

The SenderView.xaml

The implementation will be something like

 

<UserControl x:Class="BluetoothSample.Views.SenderView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             DataContext="{Binding SenderViewModel,
                                   Source={StaticResource Locator}}"
             d:DesignHeight="400"
             d:DesignWidth="400"
             mc:Ignorable="d">
    <StackPanel Margin="20" Orientation="Vertical">
        <TextBlock>I am the Sender.</TextBlock>
        <TextBlock Margin="0,20,0,0">Select one device:</TextBlock>
        <ListBox Width="200"
                 Height="100"
                 MaxWidth="200"
                 MaxHeight="100"
                 Margin="0,20,0,0"
                 HorizontalAlignment="Left"
                 ItemsSource="{Binding Devices}"
                 SelectedItem="{Binding SelectDevice}" />
        <TextBlock Margin="0,20,0,0" Text="Write the data to send:" />
        <TextBox Margin="0,20,20,0" Text="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <Button Width="80"
                Margin="0,20,20,0"
                HorizontalAlignment="Right"
                Command="{Binding SendCommand}"
                Content="Send" />
        <TextBlock Margin="0,20,0,0" TextWrapping="Wrap">
            Result:<Run Text="{Binding ResultValue}" />
        </TextBlock>
    </StackPanel>
</UserControl>

The MainWindow.xaml

The MainWindow will show/hide the user controls defined. The implementation is defined by

<Window x:Class="BluetoothSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:BluetoothSample.Views"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        Title="Bluetooth Sample" 
        MinWidth="600" MinHeight="560"
        MaxWidth="600" MaxHeight="560">
    <StackPanel Orientation="Vertical">
        <GroupBox Margin="10,10,10,0"  Header="Choose the type:">
            <StackPanel Orientation="Horizontal">
                <RadioButton Margin="20" IsChecked="{Binding IsReceiver, Mode=TwoWay}">Receiver - will receive data from Sender</RadioButton>
                <RadioButton Margin="20" IsChecked="{Binding IsSender, Mode=TwoWay}">Sender - will send data for the Receiver</RadioButton>
            </StackPanel>
        </GroupBox>
        <GroupBox Margin="10,10,10,0" Header="Dashboard">
            <StackPanel Orientation="Vertical">
                <!-- visibility binding not worked in user control and 
                for this reason was added the stackpanel for each usercontrol-->
                <StackPanel Visibility="{Binding ReceiverVisibility}">
                    <views:ReceiverView Height="390" x:Name="ReceiverView"/>
                </StackPanel>
                <StackPanel Visibility="{Binding SenderVisibility}">
                    <views:SenderView Height="390"  x:Name="SenderView" />
                </StackPanel>
            </StackPanel>
        </GroupBox>
    </StackPanel>
</Window>

Note: For have a nice look, we will add the Modern UI nugget package. For see more about it, please read the following article

Modern UI for WPF application by example (Blank Window)

 

The ViewModelLocator

The ViewModelLocator will be a static resource for the application and is defined in App.xaml, as following

 

   <vm:ViewModelLocator xmlns:vm="clr-namespace:BluetoothSample.ViewModel"
                                 x:Key="Locator"
                                 d:IsDataSource="True" />

This class is where the setup for the view model and service are made and the implementation is something like

 public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register<IReceiverBluetoothService, ReceiverBluetoothService>();
            SimpleIoc.Default.Register<ISenderBluetoothService, SenderBluetoothService>();
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<ReceiverViewModel>();
            SimpleIoc.Default.Register<SenderViewModel>();
        }

        /// <summary>
        /// Gets the main.
        /// </summary>
        /// <value>The main.</value>
        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        /// <summary>
        /// Gets the Receiver view model.
        /// </summary>
        /// <value>The Receiver view model.</value>
        public ReceiverViewModel ReceiverViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<ReceiverViewModel>();
            }
        }

        /// <summary>
        /// Gets the Sender view model.
        /// </summary>
        /// <value>The Sender view model.</value>
        public SenderViewModel SenderViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<SenderViewModel>();
            }
        }

        /// <summary>
        /// Cleanups this instance.
        /// </summary>
        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }

 

Running the application

For test the application we need two devices, where in the first device we will run as “Sender” and in the second device we will run as “Receiver”.

The “Receiver” can start listening

The “Sender” is searching for available devices

The “Receiver” is starting for listening

 

The “Sender” can select a device for send the message

 

The “Sender” will send a message for the selected device

 

The “Receiver” received data sent by “Sender”

Conclusion

In conclusion, we can conclude the 32feet.Net is a great library for get all devices around with bluetooth on and for send data through bluetooth. The library has a great documentation but could have more samples that we could run for test the features provided.

Another point that the developer should be aware is the fact the project isn´t updated since 2012 but everyone can use the source code if needed, for fix any issue.

Source Code

The complete source code can be found in

Bluetooth Sample using 32feet.Net

Credits

Thanks to Pedro Lamas and Peter Foot  for help in make it work!

See More

Authentication using Facebook, Google and Microsoft accounts in Universal Apps using MVVM

Introduction

This sample shows how to connect universal apps to Facebook, Google and Microsoft accounts using the MVVM pattern.

Building the Sample

You only need Visual Studio 2012 or Visual Studio 2013 and Windows 8 or Windows 8.1, both the RTM version.

Description

Recently published was Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM) and this sample has the same goal, but now the target is Universal Apps. Both have the goal to use the MVVM pattern.

Before starting this sample, analize to see if the SDKs used in the first sample could be used in this new sample and in a first attempt didn´t find a package for all the targets (Windows 8.1 and Windows Phone 8.1 Runtime). Let’s see that analysis.

The packages used was:

  1. Facebook SDK for Windows Phone (http://facebooksdk.net/docs/phone/ )
  2. Google APIs Auth Client and Google APIs OAuth2 Client (https://www.nuget.org/packages/Google.Apis.Auth/ andhttps://www.nuget.org/packages/Google.Apis.Authentication/1.6.0-beta )
  3. Live SDK (http://msdn.microsoft.com/en-US/onedrive/dn630256 )

The packages that were analized are:

  1. Facebook SDK for Windows Phone: there is a package for Windows 8.1 Store Apps but there isn’t for Windows Phone 8.1 Runtime (we cannot use the version from Windows Phone 8.0 because it uses a namespace for controls that does not exist in the Windows Phone 8.1 Runtime)
  2. Google APIs Auth Client and Google APIs OAuth2 Client: there is a package compatible with Windows 8.1 Store Apps but it is a bit different from the sample created before, the API changed. And there isn’t a package for the Windows Phone 8.1 Runtime.
  3. Live SDK: is compatible with the Windows Phone 8.1 Runtime and Windows 8.1.

The next step, was to try to port the package for Google from Windows 8.1 Store Apps to Windows Phone 8.1 Runtime, create the logic because there is a lot of code shared between them and then the hard work began.

After some attempts, the code started to throw the exception NotImplementedException because theWebAuthenticationBroker class does not work the same way for these targets. There is a sample that shows this difference, here is the source code  and we will see this in this sample.

In conclusion of this analysis, I decided to use WebAuthenticationBroker for authentication using Facebook and Google accounts and Live SDK for Microsoft accounts.

Let’s start the sample!

Note: This sample uses MVVM Light and Cimbalino Toolkit.

For each provider it is necessary to get the app id/client id/client secrect in their websites.

For Google go to https://console.developers.google.com/project and create a new project (APIs and auth > credentials).

Universal Apps using MVVM

For Facebook go to https://developers.facebook.com/ and create a new app.

For Live SDK go to https://account.live.com/developers/applications/index and create one or use an existing app.

Before you start you should change the Constant file to add client ids / client secret / app id, without it the app fails!!

 

/// <summary>   
    /// Defines the constants strings used in the app.   
    /// </summary>   
    public class Constants   
    {   
        /// <summary>   
        /// The google callback url.   
        /// </summary>   
       #if !WINDOWS_PHONE_APP 
       public const string GoogleCallbackUrl = "urn:ietf:wg:oauth:2.0:oob"; 
       #else 
        public const string GoogleCallbackUrl = "http://localhost"; 
       #endif  
   
        /// <summary>   
        /// The facebook app id.   
        /// </summary>   
        public const string FacebookAppId = "<app id>";   
   
        /// <summary>   
        /// The google client identifier.   
        /// </summary>   
        public const string GoogleClientId = "<client id>";   
           
        /// <summary>   
        /// The google client secret.   
        /// </summary>   
        public const string GoogleClientSecret = "<client secret";   
   
        /// <summary>   
        /// The login token.   
        /// </summary>   
        public const string LoginToken = "LoginToken";   
           
        /// <summary>   
        /// The facebook provider.   
        /// </summary>   
        public const string FacebookProvider = "facebook";   
   
        /// <summary>   
        /// The google provider.   
        /// </summary>   
        public const string GoogleProvider = "google";   
   
        /// <summary>   
        /// The microsoft provider.   
        /// </summary>   
        public const string MicrosoftProvider = "microsoft";   
    }

This sample will use the same idea used in Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM). There are classes that have the same goal and the same name. Like in that sample, this sample created a SessionService that manages the Login and Logout using a provider value. That is nice because in LoginView we set the buttons to the same command and for each command we set the provider in commandparameter. With it, the LoginView and LoginViewModel are more clear and simple.

The following are the classes created:

  • FacebookService has all code related with authentication with Facebook account;
  • MicrosoftService has all code related with authentication with Microsoft account;
  • GoogleService has all code related with authentication with Google account;
  • SessionService call the methods login or logout for the provide requested;

The Flow

To help to understood the difference in the flow in each platform some diagrams were created.

The flow for the Windows 8.1 Store apps will be:

The flow for Windows Phone 8.1 Runtime will be:

  • using Microsoft account
  • using Facebook Account or Google account

Note: Start in LoginView using the blue arrow and when the blue flow finnishes, go to FacebookService/GoogleService and follow the red flow.

Like we can see, the authentication for Windows Phone 8.1 is very complicated, it could be easier like in Windows 8.1 Store apps. And It breaks the MVVM Pattern!

This sample is a Universal App, for this reason the code can be found in the Shared Project and to add specific features for each target directives (#if #else #endif) are used, it can cause some difficulties for understanding the code but is a good way to have only one code in one place. Partial methods and classes can be used here, because in most cases directives are added to add a method for Windows Phone.

The FacebookService

/// <summary>   
    /// Defines the Facebook Service.   
    /// </summary>   
    public class FacebookService : IFacebookService   
    {   
        private readonly ILogManager _logManager;   
   
        /// <summary>   
        /// Initializes a new instance of the <see cref="FacebookService"/> class.   
        /// </summary>   
        /// <param name="logManager">   
        /// The log manager.   
        /// </param>   
        public FacebookService(ILogManager logManager)   
        {   
            _logManager = logManager;   
        }   
   
        /// <summary>   
        /// The login sync.   
        /// </summary>   
        /// <returns>   
        /// The <see cref="Task"/> object.   
        /// </returns>   
        public async Task<Session> LoginAsync()   
        {   
            const string FacebookCallbackUrl = "https://m.facebook.com/connect/login_success.html";   
            var facebookUrl = "https://www.facebook.com/dialog/oauth?client_id=" + Uri.EscapeDataString(Constants.FacebookAppId) + "&redirect_uri=" + Uri.EscapeDataString(FacebookCallbackUrl) + "&scope=public_profile,email&display=popup&response_type=token";   
   
            var startUri = new Uri(facebookUrl);   
            var endUri = new Uri(FacebookCallbackUrl);   
  
#if WINDOWS_PHONE_APP   
            WebAuthenticationBroker.AuthenticateAndContinue(startUri, endUri, null, WebAuthenticationOptions.None);   
            return null;   
#else   
            var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, startUri, endUri);   
            return GetSession(webAuthenticationResult);   
#endif   
        }   
           
        private void GetKeyValues(string webAuthResultResponseData, out string accessToken, out string expiresIn)   
        {   
            string responseData = webAuthResultResponseData.Substring(webAuthResultResponseData.IndexOf("access_token", StringComparison.Ordinal));   
            string[] keyValPairs = responseData.Split('&');   
            accessToken = null;   
            expiresIn = null;   
            for (int i = 0; i < keyValPairs.Length; i++)   
            {   
                string[] splits = keyValPairs[i].Split('=');   
                switch (splits[0])   
                {   
                    case "access_token":   
                        accessToken = splits[1];   
                        break;   
                    case "expires_in":   
                        expiresIn = splits[1];   
                        break;   
                }   
            }   
        }   
   
        /// <summary>   
        /// This function extracts access_token from the response returned from web authentication broker   
        /// and uses that token to get user information using facebook graph api.    
        /// </summary>   
        /// <param name="accessToken">   
        /// The access Token.   
        /// </param>   
        /// <returns>   
        /// The <see cref="Task"/>.   
        /// </returns>   
        private async Task<UserInfo> GetFacebookUserNameAsync(string accessToken)   
        {   
            var httpClient = new HttpClient();   
            var response = await httpClient.GetStringAsync(new Uri("https://graph.facebook.com/me?access_token=" + accessToken));   
            var value = JsonValue.Parse(response).GetObject();   
            var facebookUserName = value.GetNamedString("name");   
   
            return new UserInfo   
            {   
                Name = facebookUserName,   
            };   
        }   
   
        /// <summary>   
        /// Logouts this instance.   
        /// </summary>   
        public async void Logout()   
        {   
            Exception exception = null;   
            try   
            {   
                  
            }   
            catch (Exception ex)   
            {   
                exception = ex;   
            }   
            if (exception != null)   
            {   
                await _logManager.LogAsync(exception);   
            }   
        }   
  
#if WINDOWS_PHONE_APP   
        public async Task<Session> Finalize(WebAuthenticationBrokerContinuationEventArgs args)   
        {   
            Exception exception = null;   
            try   
            {   
                var result = args.WebAuthenticationResult;   
   
                return GetSession(result);   
            }   
            catch (Exception e)   
            {   
                exception = e;   
            }   
   
            await _logManager.LogAsync(exception);   
              
            return null;   
        }   
#endif   
        private Session GetSession(WebAuthenticationResult result)   
        {   
            if (result.ResponseStatus == WebAuthenticationStatus.Success)   
            {   
                string accessToken;   
                string expiresIn;   
                GetKeyValues(result.ResponseData, out accessToken, out expiresIn);   
   
                return new Session   
                {   
                    AccessToken = accessToken,   
                    ExpireDate = new DateTime(long.Parse(expiresIn)),   
                    Provider = Constants.FacebookProvider   
                };   
            }   
            if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)   
            {   
                throw new Exception("Error http");   
            }   
            if (result.ResponseStatus == WebAuthenticationStatus.UserCancel)   
            {   
                throw new Exception("User Canceled.");   
            }   
            return null;   
        }   
    }

In this class, in LoginAsync, we could see the directives that define the code for each platform. The first attempt was a bit complicated to define a solution for it, using null was the solution (for Windows Phone, of course!). This class doesn´t do anything in the logout method, we could remove it, but to keep the same pattern in all the classes it wasn´t removed and the session is not saved in webview, at least it is possible to login using a different account when the LoginView is shown again.

The GoogleService

public class GoogleService : IGoogleService
    {
        private readonly ILogManager _logManager;

        /// <summary>
        /// Initializes a new instance of the <see cref="GoogleService"/> class.
        /// </summary>
        /// <param name="logManager">
        /// The log manager.
        /// </param>
        public GoogleService(ILogManager logManager)
        {
            _logManager = logManager;
        }

        /// <summary>
        /// The login async.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/> object.
        /// </returns>
        public async Task<Session> LoginAsync()
        {
            var googleUrl = new StringBuilder();
            googleUrl.Append("https://accounts.google.com/o/oauth2/auth?client_id=");
            googleUrl.Append(Uri.EscapeDataString(Constants.GoogleClientId));
            googleUrl.Append("&scope=openid%20email%20profile");
            googleUrl.Append("&redirect_uri=");
            googleUrl.Append(Uri.EscapeDataString(Constants.GoogleCallbackUrl));
            googleUrl.Append("&state=foobar");
            googleUrl.Append("&response_type=code");
          
            var startUri = new Uri(googleUrl.ToString());
           

#if !WINDOWS_PHONE_APP
           var endUri = new Uri("https://accounts.google.com/o/oauth2/approval?");
           var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
           return await GetSession(webAuthenticationResult);
#else
            WebAuthenticationBroker.AuthenticateAndContinue(startUri, new Uri(Constants.GoogleCallbackUrl), null, WebAuthenticationOptions.None);
            return null;
#endif
        }
        
        private string GetCode(string webAuthResultResponseData)
        {
            // Success code=4/izytpEU6PjuO5KKPNWSB4LK3FU1c
            var split = webAuthResultResponseData.Split('&');

            return split.FirstOrDefault(value => value.Contains("code"));
        }

        /// <summary>
        /// The logout.
        /// </summary>
        public void Logout()
        {
        }

#if WINDOWS_PHONE_APP
        public async Task<Session> Finalize(WebAuthenticationBrokerContinuationEventArgs args)
        {
            Exception exception = null;
            try
            {
                return await GetSession(args.WebAuthenticationResult);
            }
            catch (Exception e)
            {
                exception = e;
            }

            await _logManager.LogAsync(exception);

            return null;
        }
#endif
        private async Task<Session> GetSession(WebAuthenticationResult result)
        {
            if (result.ResponseStatus == WebAuthenticationStatus.Success)
            {
                var code = GetCode(result.ResponseData);
                var serviceRequest = await GetToken(code);

                return new Session
                {
                    AccessToken = serviceRequest.access_token,
                    ExpireDate = new DateTime(long.Parse(serviceRequest.expires_in)),
                    Id = serviceRequest.id_token,
                    Provider = Constants.GoogleProvider
                };
            }
            if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
            {
                throw new Exception("Error http");
            }
            if (result.ResponseStatus == WebAuthenticationStatus.UserCancel)
            {
                throw new Exception("User Canceled.");
            }
            return null;
        }

        private static async Task<ServiceResponse> GetToken(string code)
        {
            
            const string TokenUrl = "https://accounts.google.com/o/oauth2/token";

            var body = new StringBuilder();
            body.Append(code);
            body.Append("&client_id=");
            body.Append(Uri.EscapeDataString(Constants.GoogleClientId));
            body.Append("&client_secret=");
            body.Append(Uri.EscapeDataString(Constants.GoogleClientSecret));
            body.Append("&redirect_uri=");
            body.Append(Uri.EscapeDataString(Constants.GoogleCallbackUrl));
            body.Append("&grant_type=authorization_code");
            
            var client = new HttpClient();
            var request = new HttpRequestMessage(HttpMethod.Post, new Uri(TokenUrl))
            {
                Content = new StringContent(body.ToString(), Encoding.UTF8, "application/x-www-form-urlencoded")
            };
            var response = await client.SendAsync(request);
            var content = await response.Content.ReadAsStringAsync();
         
            var serviceTequest = JsonConvert.DeserializeObject<ServiceResponse>(content);
            return serviceTequest;
        }
    }

 

Here, we don´t have anything new, this is similar to the FacebookService.

Note: FacebookService and GoogleService are similar but the response and request are different for the reason that these classes are not joined.

The MicrosoftService

/// <summary>   
/// The microsoft service.   
/// </summary>   
public class MicrosoftService : IMicrosoftService   
{   
    private readonly ILogManager _logManager;   
    private LiveAuthClient _authClient;   
    private LiveConnectSession _liveSession;   
  
  
    /// <summary>   
    /// Defines the scopes the application needs.   
    /// </summary>   
    private List<string> _scopes;   
       
    /// <summary>   
    /// Initializes a new instance of the <see cref="MicrosoftService"/> class.   
    /// </summary>   
    /// <param name="logManager">   
    /// The log manager.   
    /// </param>   
    public MicrosoftService(ILogManager logManager)   
    {   
        _scopes = new List<string> { "wl.signin", "wl.basic", "wl.offline_access" };   
        _logManager = logManager;   
    }   
  
    /// <summary>   
    /// The login async.   
    /// </summary>   
    /// <returns>   
    /// The <see cref="Task"/> object.   
    /// </returns>   
    public async Task<Session> LoginAsync()   
    {   
  
        Exception exception = null;   
        try   
        {   
            _authClient = new LiveAuthClient();   
            var loginResult = await _authClient.InitializeAsync(_scopes);   
            var result = await _authClient.LoginAsync(_scopes);   
            if (result.Status == LiveConnectSessionStatus.Connected)   
            {   
                _liveSession = loginResult.Session;   
                var session = new Session   
                {   
                    AccessToken = result.Session.AccessToken,   
                    Provider = Constants.MicrosoftProvider,   
                };   
  
                return session;   
            }   
  
        }   
        catch (LiveAuthException ex)   
        {   
            throw new InvalidOperationException("Login canceled.", ex);   
        }   
  
        catch (Exception e)   
        {   
            exception = e;   
        }   
         await _logManager.LogAsync(exception);   
  
        return null;   
    }   
  
    /// <summary>   
    /// The get user info.   
    /// </summary>   
    /// <returns>   
    /// The <see cref="Task"/> object.   
    /// </returns>   
    public async Task<IDictionary<string, object>> GetUserInfo()   
    {   
  
        Exception exception = null;   
        try   
        {      
            var liveClient = new LiveConnectClient(_liveSession);   
            LiveOperationResult operationResult = await liveClient.GetAsync("me");   
  
            return operationResult.Result;   
        }   
        catch (LiveConnectException e)   
        {   
            exception = e;   
        }   
        await _logManager.LogAsync(exception);   
  
        return null;   
    }   
  
    /// <summary>   
    /// The logout.   
    /// </summary>   
    public async void Logout()   
    {   
  
        if (_authClient == null)   
        {   
            _authClient = new LiveAuthClient();   
            var loginResult = await _authClient.InitializeAsync(_scopes);   
        }   
        if (_authClient.CanLogout)   
        {   
            _authClient.Logout();   
        }   
    }   
}

This class was reused from the sample Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM), but for it work is required to associate the app to the store.

That will result in an additional file in the project: as in the following:

Note

 

  1. These file are removed from the sample because each developer has their own.
  2. In devices that the Microsoft account is logged the application will do the login automatically.

The SessionService

/// <summary>   
/// The service session.   
/// </summary>   
public class SessionService : ISessionService   
{   
    private readonly IApplicationDataService _applicationSettings;   
    private readonly IFacebookService _facebookService;   
    private readonly IMicrosoftService _microsoftService;   
    private readonly IGoogleService _googleService;   
    private readonly ILogManager _logManager;   
       
    /// <summary>   
    /// Initializes a new instance of the <see cref="SessionService" /> class.   
    /// </summary>   
    /// <param name="applicationSettings">The application settings.</param>   
    /// <param name="facebookService">The facebook service.</param>   
    /// <param name="microsoftService">The microsoft service.</param>   
    /// <param name="googleService">The google service.</param>   
    /// <param name="logManager">The log manager.</param>   
    public SessionService(IApplicationDataService applicationSettings,   
        IFacebookService facebookService,   
        IMicrosoftService microsoftService,   
        IGoogleService googleService, ILogManager logManager)   
    {   
        _applicationSettings = applicationSettings;   
        _facebookService = facebookService;   
        _microsoftService = microsoftService;   
        _googleService = googleService;   
        _logManager = logManager;   
    }   
  
    /// <summary>   
    /// Gets the session.   
    /// </summary>   
    /// <returns>The session object.</returns>   
    public Session GetSession()   
    {   
        var expiryValue = DateTime.MinValue;   
        string expiryTicks = LoadEncryptedSettingValue("session_expiredate");   
        if (!string.IsNullOrWhiteSpace(expiryTicks))   
        {   
            long expiryTicksValue;   
            if (long.TryParse(expiryTicks, out expiryTicksValue))   
            {   
                expiryValue = new DateTime(expiryTicksValue);   
            }   
        }   
  
        var session = new Session   
        {   
            AccessToken = LoadEncryptedSettingValue("session_token"),   
            Id = LoadEncryptedSettingValue("session_id"),   
            ExpireDate = expiryValue,   
            Provider = LoadEncryptedSettingValue("session_provider")   
        };   
        _applicationSettings.LocalSettings[Constants.LoginToken] = true;   
        return session;   
    }   
  
    /// <summary>   
    /// The save session.   
    /// </summary>   
    /// <param name="session">   
    /// The session.   
    /// </param>   
    private void Save(Session session)   
    {   
        SaveEncryptedSettingValue("session_token", session.AccessToken);   
        SaveEncryptedSettingValue("session_id", session.Id);   
        SaveEncryptedSettingValue("session_expiredate", session.ExpireDate.Ticks.ToString(CultureInfo.InvariantCulture));   
        SaveEncryptedSettingValue("session_provider", session.Provider);   
        _applicationSettings.LocalSettings[Constants.LoginToken] = true;   
    }   
  
    /// <summary>   
    /// The clean session.   
    /// </summary>   
    private void CleanSession()   
    {   
        _applicationSettings.LocalSettings.Remove("session_token");   
        _applicationSettings.LocalSettings.Remove("session_id");   
        _applicationSettings.LocalSettings.Remove("session_expiredate");   
        _applicationSettings.LocalSettings.Remove("session_provider");   
        _applicationSettings.LocalSettings.Remove(Constants.LoginToken);   
    }   
  
    /// <summary>   
    /// The login async.   
    /// </summary>   
    /// <param name="provider">   
    /// The provider.   
    /// </param>   
    /// <returns>   
    /// The <see cref="Task"/> object.   
    /// </returns>   
    public async Task<bool?> LoginAsync(string provider)   
    {   
        Provider = provider;   
        Exception exception;   
        try   
        {   
            Session session = null;   
            switch (provider)   
            {   
                case Constants.FacebookProvider:   
                    session = await _facebookService.LoginAsync();   
                    break;   
                case Constants.MicrosoftProvider:   
                    session = await _microsoftService.LoginAsync();   
                    break;   
                case Constants.GoogleProvider:   
                    session = await _googleService.LoginAsync();   
                    break;   
            }   
            if (session == null)   
            {   
                return null;   
            }   
            Save(session);   
               
            return true;   
        }   
        catch (Exception ex)   
        {   
            exception = ex;   
        }   
        await _logManager.LogAsync(exception);   
  
        return false;   
    }   
  
    /// <summary>   
    /// Gets or sets the provider.   
    /// </summary>   
    /// <value>   
    /// The provider.   
    /// </value>   
    public string Provider { get; set; }   
  
    /// <summary>   
    /// The logout.   
    /// </summary>   
    public async void Logout()   
    {   
        Exception exception = null;   
        try   
        {   
            var session = GetSession();   
            switch (session.Provider)   
            {   
                case Constants.FacebookProvider:   
                    _facebookService.Logout();   
                    break;   
                case Constants.MicrosoftProvider:   
                    _microsoftService.Logout();   
                    break;   
                case Constants.GoogleProvider:   
                    _googleService.Logout();   
                    break;   
            }   
            CleanSession();   
        }   
        catch (Exception ex)   
        {   
            exception = ex;   
        }   
        if (exception != null)   
        {   
            await _logManager.LogAsync(exception);   
        }   
    }   
  
WINDOWS_PHONE_APP   
    public async Task<bool> Finalize(WebAuthenticationBrokerContinuationEventArgs args)   
    {   
        Exception exception = null;   
        try   
        {   
            Session session = null;   
            switch (Provider)   
            {   
                case Constants.FacebookProvider:   
                    session = await _facebookService.Finalize(args);   
                    break;   
                case Constants.GoogleProvider:   
                    session = await _googleService.Finalize(args);   
                    break;   
            }   
            Save(session);   
            return true;   
        }   
        catch (Exception e)   
        {   
            exception = e;   
        }   
        await _logManager.LogAsync(exception);   
        return false;   
    }   
if   
    /// <summary>   
    /// Loads an encrypted setting value for a given key.   
    /// </summary>   
    /// <param name="key">   
    /// The key to load.   
    /// </param>   
    /// <returns>   
    /// The value of the key.   
    /// </returns>   
    private string LoadEncryptedSettingValue(string key)   
    {   
        string value = null;   
  
        var protectedBytes = _applicationSettings.LocalSettings[key];   
        if (protectedBytes != null)   
        {   
            // todo use DataProtectionProvider   
            // byte[] valueBytes = ProtectedData.Unprotect(protectedBytes, null);   
            // value = Encoding.UTF8.GetString(valueBytes, 0, valueBytes.Length);   
            value = protectedBytes.ToString();   
        }   
  
        return value;   
    }   
  
    /// <summary>   
    /// Saves a setting value against a given key, encrypted.   
    /// </summary>   
    /// <param name="key">   
    /// The key to save against.   
    /// </param>   
    /// <param name="value">   
    /// The value to save against.   
    /// </param>   
    /// <exception cref="System.ArgumentOutOfRangeException">   
    /// The key or value provided is unexpected.   
    /// </exception>   
    private void SaveEncryptedSettingValue(string key, string value)   
    {   
        if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value))   
        {   
            // todo use DataProtectionProvider   
            // byte[] valueBytes = Encoding.UTF8.GetBytes(value);   
            // var dataProtectionProvider = new DataProtectionProvider();             
            // // Encrypt the value by using the Protect() method.   
            // byte[] protectedBytes = await dataProtectionProvider.ProtectAsync(valueBytes);   
            _applicationSettings.LocalSettings[key] = value;   
        }   
    }   
}

This class will return null if the session is null, we need to be aware that there isn´t any error and the session is returned or if it is null, the process will be done by the Finalize method.

Here is the class diagram with all classes and interfaces created in this sample:

Now to build the User Interface and because we are using the MVVM pattern, it created a LoginViewModel to do the binding to the LoginView.

The LoginViewModel

/// <summary>   
    /// The login view model.   
    /// </summary>   
    public class LoginViewModel : ViewModelBase   
    {   
        private readonly ILogManager _logManager;   
        private readonly IMessageBoxService _messageBox;   
        private readonly INetworkInformationService _networkInformationService;   
        private readonly INavigationService _navigationService;   
        private readonly ISessionService _sessionService;   
        private bool _inProgress;   
   
        /// <summary>   
        /// Initializes a new instance of the <see cref="LoginViewModel"/> class.   
        /// </summary>   
        /// <param name="navigationService">   
        /// The navigation service.   
        /// </param>   
        /// <param name="sessionService">   
        /// The session service.   
        /// </param>   
        /// <param name="messageBox">   
        /// The message box.   
        /// </param>   
        /// <param name="networkInformationService">   
        /// The network connection.   
        /// </param>   
        /// <param name="logManager">   
        /// The log manager.   
        /// </param>   
        public LoginViewModel(INavigationService navigationService,   
            ISessionService sessionService,   
            IMessageBoxService messageBox,   
            INetworkInformationService networkInformationService,   
            ILogManager logManager)   
        {   
            _navigationService = navigationService;   
            _sessionService = sessionService;   
            _messageBox = messageBox;   
            _networkInformationService = networkInformationService;   
   
            _logManager = logManager;   
            LoginCommand = new RelayCommand<string>(LoginAction);   
        }   
   
        /// <summary>   
        /// Gets the navigation service.   
        /// </summary>   
        /// <value>   
        /// The navigation service.   
        /// </value>   
        public INavigationService NavigationService   
        {   
            get { return _navigationService; }   
        }   
   
        /// <summary>   
        /// Gets or sets a value indicating whether in progress.   
        /// </summary>   
        /// <value>   
        /// The in progress.   
        /// </value>   
        public bool InProgress   
        {   
            get { return _inProgress; }   
            set { Set(() => InProgress, ref _inProgress, value); }   
        }   
   
        /// <summary>   
        /// Gets the facebook login command.   
        /// </summary>   
        /// <value>   
        /// The facebook login command.   
        /// </value>   
        public ICommand LoginCommand { get; private set; }   
   
        /// <summary>   
        /// Facebook's login action.   
        /// </summary>   
        /// <param name="provider">   
        /// The provider.   
        /// </param>   
        private async void LoginAction(string provider)   
        {   
            Exception exception = null;   
            bool isToShowMessage = false;   
            try   
            {   
                if (!_networkInformationService.IsNetworkAvailable)   
                {   
                    await _messageBox.ShowAsync("There isn´t network connection.",   
                                          "Authentication Sample",   
                                          new List<string> { "Ok" });   
                    return;   
                }   
                if (Constants.GoogleClientId.Contains("<") || Constants.GoogleClientSecret.Contains("<"))   
                {   
                    await _messageBox.ShowAsync("Is missing the google client id and client secret. Search for Constant.cs file.",   
                                         "Authentication Sample",   
                                         new List<string> { "Ok" });   
                    return;   
                }   
                if (Constants.FacebookAppId.Contains("<"))   
                {   
                    await _messageBox.ShowAsync("Is missing the facebook client id. Search for Constant.cs file.",   
                                         "Authentication Sample",   
                                         new List<string> { "Ok" });   
                    return;   
                }   
                InProgress = true;   
                var auth = await _sessionService.LoginAsync(provider);   
                if (auth == null)   
                {   
                    return;   
                }   
                if (!auth.Value)   
                {   
                    await ShowMessage();   
                }   
                else   
                {   
                    _navigationService.Navigate<MainView>();   
                    InProgress = false;   
                }   
   
                InProgress = false;   
            }   
            catch (Exception ex)   
            {   
                InProgress = false;   
                exception = ex;   
                isToShowMessage = true;   
            }   
            if (isToShowMessage)   
            {   
                await _messageBox.ShowAsync("Application fails.",   
                                           "Authentication Sample",    
                                            new List<string> { "Ok" });   
            }   
            if (exception != null)   
            {   
                await _logManager.LogAsync(exception);   
            }   
        }   
   
        private async Task ShowMessage()   
        {   
            await _messageBox.ShowAsync("Wasn´t possible to complete the login.",   
               "Authentication Sample",   
                new List<string>   
                {   
                   "Ok"    
                });   
        }   
  
#if WINDOWS_PHONE_APP   
        public async void Finalize(WebAuthenticationBrokerContinuationEventArgs args)   
        {   
            var result = await _sessionService.Finalize(args);   
            if (!result)   
            {   
                await ShowMessage();   
            }   
            else   
            {   
                _navigationService.Navigate<MainView>();   
                InProgress = false;   
            }   
        }   
#endif   
    }

Note: In the LoginAction the parameter provider is the value of the CommandParameter received in LoginCommand, this is set in the login page.

For the final the code, let’s see the code for the page.

The LoginPage.xaml

<Page   
    x:Class="AuthenticationSample.UniversalApps.Views.LoginView"   
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
    xmlns:converters="using:Cimbalino.Toolkit.Converters"   
    mc:Ignorable="d">   
   
    <Page.DataContext>   
        <Binding Mode="OneWay"   
                 Path="LoginViewModel"   
                 Source="{StaticResource Locator}" />   
    </Page.DataContext>   
    <Page.Resources>   
        <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>   
    </Page.Resources>   
    <!--  LayoutRoot is the root grid where all page content is placed  -->   
    <Grid x:Name="LayoutRoot" Background="LightGray">   
        <Grid.RowDefinitions>   
            <RowDefinition Height="{StaticResource HeaderHeigth}" />   
            <RowDefinition Height="*" />   
        </Grid.RowDefinitions>   
   
        <!--  TitlePanel contains the name of the application and page title  -->   
        <StackPanel x:Name="TitlePanel"    
                    Margin="{StaticResource HeaderMargin}"   
                    VerticalAlignment="Center" Grid.Row="0">   
            <TextBlock  FontSize="30"    
                        Foreground="Black"   
                       Text="Login"/>   
        </StackPanel>   
   
        <!--  ContentPanel - place additional content here  -->   
        <Grid x:Name="ContentPanel"   
              VerticalAlignment="Center"   
              HorizontalAlignment="Center"   
              Grid.Row="1">   
            <Grid.RowDefinitions>   
                <RowDefinition Height="Auto" />   
                <RowDefinition Height="80" />   
                <RowDefinition Height="80" />   
                <RowDefinition Height="80" />   
                <RowDefinition Height="80" />   
            </Grid.RowDefinitions>   
            <TextBlock Grid.Row="0"    
                       FontSize="20"   
                       Foreground="Black"   
                       Text="Use your account"/>   
            <Button Grid.Row="1" Width="300"   
                    Margin="10"   
                    Command="{Binding LoginCommand}"   
                    CommandParameter="facebook"   
                    Background="{StaticResource FacebookBlueBackgroundBrush}" >   
                <StackPanel Orientation="Horizontal">   
                    <TextBlock Text="ï‚š"    
                                   VerticalAlignment="Center"   
                                   FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"    
                                   HorizontalAlignment="Left"/>   
                    <TextBlock Margin="10,0,0,0" Text="Facebook"    
                           HorizontalAlignment="Center"/>   
                </StackPanel>   
            </Button>   
            <Button Grid.Row="3"   
                    Margin="10" Width="300"   
                    Command="{Binding LoginCommand}"   
                    CommandParameter="microsoft"   
                    Background="{StaticResource MicrosoftBlueBackgroundBrush}" >   
                <StackPanel Orientation="Horizontal">   
                    <TextBlock Text="ï…º"   
                                   VerticalAlignment="Center"   
                                   FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"    
                                   HorizontalAlignment="Left"/>   
                    <TextBlock Margin="10,0,0,0" Text="Microsoft"    
                           HorizontalAlignment="Center"/>   
                </StackPanel>   
            </Button>   
            <Button Grid.Row="2" Width="300"   
                    Margin="10"   
                    Command="{Binding LoginCommand}"   
                    CommandParameter="google"   
                    Background="{StaticResource GoogleRedBackgroundBrush}" >   
                <StackPanel Orientation="Horizontal">   
                    <TextBlock Text=""   
                                   VerticalAlignment="Center"   
                                   FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"    
                                   HorizontalAlignment="Left"/>   
                    <TextBlock Margin="10,0,0,0" Text="Google"    
                           HorizontalAlignment="Center"/>   
                </StackPanel>   
            </Button>   
        </Grid>   
        <Grid Visibility="{Binding InProgress, Converter={StaticResource BooleanToVisibilityConverter}}"   
            Grid.Row="0"   
                   Grid.RowSpan="2">   
            <Rectangle    
                   Fill="Black"   
                   Opacity="0.75" />   
            <TextBlock    
                   HorizontalAlignment="Center"   
                   VerticalAlignment="Center"   
                   Text="Auth..." />   
            <ProgressBar IsIndeterminate="True" IsEnabled="True" Margin="0,60,0,0"/>   
        </Grid>   
   
    </Grid>   
</Page>

For use in the same page in Windows 8.1 Store apps and Windows Phone 8.1 Runtime was used some style.

The LoginView.xaml.cs

/// <summary>   
    /// An empty page that can be used on its own or navigated to within a Frame.   
    /// </summary>   
#if WINDOWS_PHONE_APP   
       public sealed partial class LoginView : Page, IWebAuthenticationContinuable   
#else   
      public sealed partial class LoginView : Page   
#endif   
    {   
        /// <summary>   
        /// Initializes a new instance of the <see cref="LoginView"/> class.   
        /// </summary>   
        public LoginView()   
        {   
            InitializeComponent();   
        }   
   
        /// <summary>   
        /// The on navigated to.   
        /// </summary>   
        /// <param name="e">   
        /// The e.   
        /// </param>   
        protected override void OnNavigatedTo(NavigationEventArgs e)   
        {   
            var viewModel = (LoginViewModel)DataContext;   
            viewModel.NavigationService.RemoveBackEntry();   
            base.OnNavigatedTo(e);   
        }   
  
#if WINDOWS_PHONE_APP   
   
        /// <summary>   
        /// Continues the web authentication.   
        /// </summary>   
        /// <param name="args">The <see cref="Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs"/> instance containing the event data.</param>   
        public void ContinueWebAuthentication(Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)   
        {   
            var viewModel = (LoginViewModel)DataContext;   
            viewModel.Finalize(args);   
        }   
#endif   
    }

Conclusion

In conclusion, the flow is completely different for each platform and it has some impact in the development and in sharing the code. Maybe, for this reason there isn’t any package for Windows Phone 8.1 that uses authentication.

Source Code

The source code can be found here.

Source Code Files

  • IFacebookService interface for FacebookService
  • IGoogleService interface for GoogleService
  • ILogManager interface for LogManager
  • IMicrosoftService interface for MicrosoftService
  • ISessionProvider interface for all providers interface (common methods)
  • ISessionService for SessionService
  • Session class for save the information about the session (provider, token, expired date)
  • FacebookService class that has all logic about the login / logout using Facebook provider
  • GoogleService class that has all logic about the login / logout using Google provider
  • MicrosoftService class that has all logic about the login / logout using Microsoft provider
  • SessionService class for manage login/logout (it will use all services provides described before)
  • LoginViewModel class for binding to LoginView.xaml
  • LoginView class that represent the page for login
  • MainViewModel class for binding to MainView.xaml
  • MainView class that appear after the login is ok
  • ViewModelLocator class contains static references to all the view model in application and provides an entry point for the bindings.

The solution

Build the Sample

  1. Start Visual Studio Express 2012 for Windows 8 and select File > Open > Project/Solution.
  2. Go to the directory in which you unzipped the sample. Go to the directory named for the sample and double-click the Visual Studio Express 2012 for Windows 8 Solution (.sln) file.
  3. Press F7 or use Build > Build Solution to build the sample.

Note: you can use Visual Studio 2013 in Windows 8.1.

Run the sample

To debug the app and then run it, press F5 or use Debug > Start Debugging. To run the app without debugging, press Ctrl+F5 or use Debug > Start Without Debugging

Output

The output for each target is:

  • Windows 8.1 Store App

Login Page

Microsoft Authentication

Facebook Authentication

Google Authentication

  • Windows Phone 8.1 App

Login View

Facebook Authentication

Google Authentication

 

Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM)

This sample show to connect one Windows Phone 8.0 app to Facebook, Google and Microsoft account. Main Features: Login/Logout and has an about page with feedback, share in social networks, review and share by email.

Download C# (19.4 MB)

Introduction

This sample show how to connect one Windows Phone 8.0 app to Facebook, Google and Microsoft account.

Main Features: Login/Logout andhas an about page with feedback, share in social networks, review and share by email.

Building the Sample

You only need Visual Studio 2012/Visual Studio 2013 and Windows 8/Windows 8.1, both the RTM version.

This sample requires the installation for Live SDK (http://msdn.microsoft.com/en-US/onedrive/dn630256). 

Description

This sample show to connect one Windows Phone 8.0 app to Facebook, Google and Microsoft account.

Main Features:

– Login/Logout (for logout i added some wourkarround to fixes the logout providers from sdks!)

– About page with feedback, share in social networks, review and share by email (not important here, but is incluided in code)

Note: This sample uses MVVM Light and Cimbalino Windows Phone Toolkit.

For this sample was used:

For each provider is needed to get the app id/client id/client secrect in their websites:

For Google go to https://console.developers.google.com/project and create a new project (Apis & auth > credentials)


For Facebook go to https://developers.facebook.com/ and create a new app.

For Live SDK go to https://account.live.com/developers/applications/index and create one or use an existing app

Before you start you should change the Constant file to add client ids / client secret / app id, without it the app fails!!

This file is inside the Resource folder.

  /// <summary> 
    /// Defines the constants strings used in the app. 
    /// </summary> 
    public class Constants 
    { 
        /// <summary> 
        /// The facebook app id. 
        /// </summary> 
        public const string FacebookAppId = "<app id>"; 
 
        /// <summary> 
        /// The google client identifier. 
        /// </summary> 
        public const string GoogleClientId = "<client id>"; 
 
        /// <summary> 
        /// The google token file name. 
        /// </summary> 
        public const string GoogleTokenFileName = "Google.Apis.Auth.OAuth2.Responses.TokenResponse-user"; 
 
        /// <summary> 
        /// The google client secret. 
        /// </summary> 
        public const string GoogleClientSecret = "<client secret>"; 
 
        /// <summary> 
        /// The microsoft client identifier. 
        /// </summary> 
        public const string MicrosoftClientId = "<client id>"; 
 
      ... 
    }

Now let’s see how to connect to each provide. For help, i created a SessionService that manage the Login and Logout using a provider value, this is nice because in LoginView i set the buttons to the same command and for each command i set the provider in commandparameter. With it the LoginView and LoginViewModel are more clear and simple. Another thing is for example if i need to connect to my server to accept the user i can do it in session manager after the authentication, without to add the code to each provider.

The classes created:

  • FacebookService has all code related with authentication with Facebook account;
  • MicrosoftService has all code related with authentication with Microsoft account;
  • GoogleService has all code related with authentication with Google account;
  • SessionService call the methods login or logout for the provide requested;

The FacebookService is:

 /// <summary> 
    /// Defines the Facebook Service. 
    /// </summary> 
    public class FacebookService : IFacebookService 
    { 
        private readonly ILogManager _logManager; 
        private readonly FacebookSessionClient _facebookSessionClient; 
 
        /// <summary> 
        /// Initializes a new instance of the <see cref="FacebookService"/> class. 
        /// </summary> 
        /// <param name="logManager"> 
        /// The log manager. 
        /// </param> 
        public FacebookService(ILogManager logManager) 
        { 
            _logManager = logManager; 
            _facebookSessionClient = new FacebookSessionClient(Constants.FacebookAppId); 
        } 
 
        /// <summary> 
        /// The login sync. 
        /// </summary> 
        /// <returns> 
        /// The <see cref="Task"/> object. 
        /// </returns> 
        public async Task<Session> LoginAsync() 
        { 
            Exception exception; 
            Session sessionToReturn = null; 
            try 
            { 
                var session = await _facebookSessionClient.LoginAsync("user_about_me,read_stream"); 
                sessionToReturn = new Session 
                { 
                    AccessToken = session.AccessToken, 
                    Id = session.FacebookId, 
                    ExpireDate = session.Expires, 
                    Provider = Constants.FacebookProvider 
                }; 
 
                return sessionToReturn; 
            } 
            catch (InvalidOperationException) 
            { 
                throw; 
            } 
            catch (Exception ex) 
            { 
                exception = ex; 
            } 
            await _logManager.LogAsync(exception); 
            return sessionToReturn; 
        } 
 
        /// <summary> 
        /// Logouts this instance. 
        /// </summary> 
        public async void Logout() 
        { 
            Exception exception = null; 
            try 
            { 
                _facebookSessionClient.Logout(); 
 
                // clean all cookies from browser, is a workarround 
                await new WebBrowser().ClearCookiesAsync(); 
            } 
            catch (Exception ex) 
            { 
                exception = ex; 
            } 
            if (exception != null) 
            { 
                await _logManager.LogAsync(exception); 
            } 
        } 
    }

Note:  In logout i added a workarround to clear all cookies in browser, if i don´t this in the first time you can login with account you want but in the next time it will use the account used in last login.

The GoogleService is:

/// <summary> 
    /// The google service. 
    /// </summary> 
    public class GoogleService : IGoogleService 
    { 
        private readonly ILogManager _logManager; 
        private readonly IStorageService _storageService; 
        private UserCredential _credential; 
        private Oauth2Service _authService; 
        private Userinfoplus _userinfoplus; 
 
        /// <summary> 
        /// Initializes a new instance of the <see cref="GoogleService" /> class. 
        /// </summary> 
        /// <param name="logManager">The log manager.</param> 
        /// <param name="storageService">The storage service.</param> 
        public GoogleService(ILogManager logManager, IStorageService storageService) 
        { 
            _logManager = logManager; 
            _storageService = storageService; 
        } 
 
        /// <summary> 
        /// The login async. 
        /// </summary> 
        /// <returns> 
        /// The <see cref="Task"/> object. 
        /// </returns> 
        public async Task<Session> LoginAsync() 
        { 
            Exception exception = null; 
            try 
            { 
                // Oauth2Service.Scope.UserinfoEmail 
                _credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets 
                { 
                    ClientId = Constants.GoogleClientId, 
                    ClientSecret = Constants.GoogleClientSecret 
                }, new[] { Oauth2Service.Scope.UserinfoProfile }, "user", CancellationToken.None); 
                 
                var session = new Session 
                { 
                    AccessToken = _credential.Token.AccessToken, 
                    Provider = Constants.GoogleProvider, 
                    ExpireDate = 
                        _credential.Token.ExpiresInSeconds != null 
                            ? new DateTime(_credential.Token.ExpiresInSeconds.Value) 
                            : DateTime.Now.AddYears(1), 
                    Id = string.Empty 
                }; 
 
                return session; 
            } 
            catch (TaskCanceledException taskCanceledException) 
            { 
                throw new InvalidOperationException("Login canceled.", taskCanceledException); 
            } 
            catch (Exception ex) 
            { 
                exception = ex; 
            } 
            await _logManager.LogAsync(exception); 
            return null; 
        } 
 
        /// <summary> 
        /// Gets the user information. 
        /// </summary> 
        /// <returns> 
        /// The user info. 
        /// </returns> 
        public async Task<Userinfoplus> GetUserInfo() 
        { 
            _authService = new Oauth2Service(new BaseClientService.Initializer() 
            { 
                HttpClientInitializer = _credential, 
                ApplicationName = AppResources.ApplicationTitle, 
            }); 
            _userinfoplus = await _authService.Userinfo.V2.Me.Get().ExecuteAsync(); 
 
            return _userinfoplus; 
        } 
 
        /// <summary> 
        /// The logout. 
        /// </summary> 
        public async void Logout() 
        { 
            await new WebBrowser().ClearCookiesAsync(); 
            if (_storageService.FileExists(Constants.GoogleTokenFileName)) 
            { 
                _storageService.DeleteFile(Constants.GoogleTokenFileName); 
            } 
        } 
    }

Note: In the logout for Google provide there isn´t a logout method, the solution is to remove all cookies and remove the file created in login operation.

 The MicrosoftService is:

 /// <summary> 
    /// The microsoft service. 
    /// </summary> 
    public class MicrosoftService : IMicrosoftService 
    { 
        private readonly ILogManager _logManager; 
        private LiveAuthClient _authClient; 
        private LiveConnectSession _liveSession; 
 
        /// <summary> 
        /// Defines the scopes the application needs. 
        /// </summary> 
        private static readonly string[] Scopes = { "wl.signin", "wl.basic", "wl.offline_access" }; 
         
        /// <summary> 
        /// Initializes a new instance of the <see cref="MicrosoftService"/> class. 
        /// </summary> 
        /// <param name="logManager"> 
        /// The log manager. 
        /// </param> 
        public MicrosoftService(ILogManager logManager) 
        { 
            _logManager = logManager; 
        } 
 
        /// <summary> 
        /// The login async. 
        /// </summary> 
        /// <returns> 
        /// The <see cref="Task"/> object. 
        /// </returns> 
        public async Task<Session> LoginAsync() 
        { 
            Exception exception = null; 
            try 
            { 
                _authClient = new LiveAuthClient(Constants.MicrosoftClientId); 
                var loginResult = await _authClient.InitializeAsync(Scopes); 
                var result = await _authClient.LoginAsync(Scopes); 
                if (result.Status == LiveConnectSessionStatus.Connected) 
                { 
                    _liveSession = loginResult.Session; 
                    var session = new Session 
                    { 
                        AccessToken = result.Session.AccessToken, 
                        ExpireDate = result.Session.Expires.DateTime, 
                        Provider = Constants.MicrosoftProvider, 
                    }; 
 
                    return session; 
                } 
            } 
            catch (LiveAuthException ex) 
            { 
                throw new InvalidOperationException("Login canceled.", ex); 
            } 
            catch (Exception e) 
            { 
                exception = e; 
            } 
 
            await _logManager.LogAsync(exception); 
            return null; 
        } 
 
        /// <summary> 
        /// The logout. 
        /// </summary> 
        public async void Logout() 
        { 
            if (_authClient == null) 
            { 
                _authClient = new LiveAuthClient(Constants.MicrosoftClientId); 
                var loginResult = await _authClient.InitializeAsync(Scopes); 
            } 
 
            _authClient.Logout(); 
        } 
    }

  The SessionService is:

/// <summary> 
    /// The service session. 
    /// </summary> 
    public class SessionService : ISessionService 
    { 
        private readonly IApplicationSettingsService _applicationSettings; 
        private readonly IFacebookService _facebookService; 
        private readonly IMicrosoftService _microsoftService; 
        private readonly IGoogleService _googleService; 
        private readonly ILogManager _logManager; 
         
        /// <summary> 
        /// Initializes a new instance of the <see cref="SessionService" /> class. 
        /// </summary> 
        /// <param name="applicationSettings">The application settings.</param> 
        /// <param name="facebookService">The facebook service.</param> 
        /// <param name="microsoftService">The microsoft service.</param> 
        /// <param name="googleService">The google service.</param> 
        /// <param name="logManager">The log manager.</param> 
        public SessionService(IApplicationSettingsService applicationSettings, 
            IFacebookService facebookService, 
            IMicrosoftService microsoftService, 
            IGoogleService googleService, ILogManager logManager) 
        { 
            _applicationSettings = applicationSettings; 
            _facebookService = facebookService; 
            _microsoftService = microsoftService; 
            _googleService = googleService; 
            _logManager = logManager; 
        } 
 
        /// <summary> 
        /// Gets the session. 
        /// </summary> 
        /// <returns>The session object.</returns> 
        public Session GetSession() 
        { 
            var expiryValue = DateTime.MinValue; 
            string expiryTicks = LoadEncryptedSettingValue("session_expiredate"); 
            if (!string.IsNullOrWhiteSpace(expiryTicks)) 
            { 
                long expiryTicksValue; 
                if (long.TryParse(expiryTicks, out expiryTicksValue)) 
                { 
                    expiryValue = new DateTime(expiryTicksValue); 
                } 
            } 
 
            var session = new Session 
            { 
                AccessToken = LoadEncryptedSettingValue("session_token"), 
                Id = LoadEncryptedSettingValue("session_id"), 
                ExpireDate = expiryValue, 
                Provider = LoadEncryptedSettingValue("session_provider") 
            }; 
 
            _applicationSettings.Set(Constants.LoginToken, true); 
            _applicationSettings.Save(); 
            return session; 
        } 
 
        /// <summary> 
        /// The save session. 
        /// </summary> 
        /// <param name="session"> 
        /// The session. 
        /// </param> 
        private void Save(Session session) 
        { 
            SaveEncryptedSettingValue("session_token", session.AccessToken); 
            SaveEncryptedSettingValue("session_id", session.Id); 
            SaveEncryptedSettingValue("session_expiredate", session.ExpireDate.Ticks.ToString(CultureInfo.InvariantCulture)); 
            SaveEncryptedSettingValue("session_provider", session.Provider); 
            _applicationSettings.Set(Constants.LoginToken, true); 
            _applicationSettings.Save(); 
        } 
 
        /// <summary> 
        /// The clean session. 
        /// </summary> 
        private void CleanSession() 
        { 
            _applicationSettings.Reset("session_token"); 
            _applicationSettings.Reset("session_id"); 
            _applicationSettings.Reset("session_expiredate"); 
            _applicationSettings.Reset("session_provider"); 
            _applicationSettings.Reset(Constants.LoginToken); 
            _applicationSettings.Save(); 
        } 
 
        /// <summary> 
        /// The login async. 
        /// </summary> 
        /// <param name="provider"> 
        /// The provider. 
        /// </param> 
        /// <returns> 
        /// The <see cref="Task"/> object. 
        /// </returns> 
        public async Task<bool> LoginAsync(string provider) 
        { 
            Exception exception = null; 
            try 
            { 
                Session session = null; 
                switch (provider) 
                { 
                    case Constants.FacebookProvider: 
                        session = await _facebookService.LoginAsync(); 
                        break; 
                    case Constants.MicrosoftProvider: 
                        session = await _microsoftService.LoginAsync(); 
                        break; 
                    case Constants.GoogleProvider: 
                        session = await _googleService.LoginAsync(); 
                        break; 
                } 
                if (session != null) 
                { 
                    Save(session); 
                } 
 
                return true; 
            } 
            catch (InvalidOperationException e) 
            { 
                throw; 
            } 
            catch (Exception ex) 
            { 
                exception = ex; 
            } 
            await _logManager.LogAsync(exception); 
 
            return false; 
        } 
 
        /// <summary> 
        /// The logout. 
        /// </summary> 
        public async void Logout() 
        { 
            Exception exception = null; 
            try 
            { 
                var session = GetSession(); 
                switch (session.Provider) 
                { 
                    case Constants.FacebookProvider: 
                        _facebookService.Logout(); 
                        break; 
                    case Constants.MicrosoftProvider: 
                        _microsoftService.Logout(); 
                        break; 
                    case Constants.GoogleProvider: 
                        _googleService.Logout(); 
                        break; 
                } 
                CleanSession(); 
            } 
            catch (Exception ex) 
            { 
                exception = ex; 
            } 
            if (exception != null) 
            { 
                await _logManager.LogAsync(exception); 
            } 
        } 
 
        /// <summary> 
        /// Loads an encrypted setting value for a given key. 
        /// </summary> 
        /// <param name="key"> 
        /// The key to load. 
        /// </param> 
        /// <returns> 
        /// The value of the key. 
        /// </returns> 
        private string LoadEncryptedSettingValue(string key) 
        { 
            string value = null; 
 
            var protectedBytes = _applicationSettings.Get<byte[]>(key); 
            if (protectedBytes != null) 
            { 
                byte[] valueBytes = ProtectedData.Unprotect(protectedBytes, null); 
                value = Encoding.UTF8.GetString(valueBytes, 0, valueBytes.Length); 
            } 
 
            return value; 
        } 
 
        /// <summary> 
        /// Saves a setting value against a given key, encrypted. 
        /// </summary> 
        /// <param name="key"> 
        /// The key to save against. 
        /// </param> 
        /// <param name="value"> 
        /// The value to save against. 
        /// </param> 
        /// <exception cref="System.ArgumentOutOfRangeException"> 
        /// The key or value provided is unexpected. 
        /// </exception> 
        private void SaveEncryptedSettingValue(string key, string value) 
        { 
            if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value)) 
            { 
                byte[] valueBytes = Encoding.UTF8.GetBytes(value); 
 
                // Encrypt the value by using the Protect() method. 
                byte[] protectedBytes = ProtectedData.Protect(valueBytes, null); 
                _applicationSettings.Set(key, protectedBytes); 
                _applicationSettings.Save(); 
            } 
        } 
    }

Now is time to build the User Interface, and because i am using MVVM, i created a LoginViewModel to binding to the LoginView.

The LoginViewModel is:

 /// <summary> 
    /// The login view model. 
    /// </summary> 
    public class LoginViewModel : ViewModelBase 
    { 
        private readonly ILogManager _logManager; 
        private readonly IMessageBoxService _messageBox; 
        private readonly INavigationService _navigationService; 
        private readonly ISessionService _sessionService; 
        private bool _inProgress; 
 
        /// <summary> 
        /// Initializes a new instance of the <see cref="LoginViewModel"/> class. 
        /// </summary> 
        /// <param name="navigationService"> 
        /// The navigation service. 
        /// </param> 
        /// <param name="sessionService"> 
        /// The session service. 
        /// </param> 
        /// <param name="messageBox"> 
        /// The message box. 
        /// </param> 
        /// <param name="logManager"> 
        /// The log manager. 
        /// </param> 
        public LoginViewModel(INavigationService navigationService, 
            ISessionService sessionService, 
            IMessageBoxService messageBox, 
            ILogManager logManager) 
        { 
            _navigationService = navigationService; 
            _sessionService = sessionService; 
            _messageBox = messageBox; 
            _logManager = logManager; 
            LoginCommand = new RelayCommand<string>(LoginAction); 
        } 
 
        /// <summary> 
        /// Gets or sets a value indicating whether in progress. 
        /// </summary> 
        /// <value> 
        /// The in progress. 
        /// </value> 
        public bool InProgress 
        { 
            get { return _inProgress; } 
            set { Set(() => InProgress, ref _inProgress, value); } 
        } 
 
        /// <summary> 
        /// Gets the facebook login command. 
        /// </summary> 
        /// <value> 
        /// The facebook login command. 
        /// </value> 
        public ICommand LoginCommand { get; private set; } 
 
        /// <summary> 
        /// Facebook's login action. 
        /// </summary> 
        /// <param name="provider"> 
        /// The provider. 
        /// </param> 
        private async void LoginAction(string provider) 
        { 
            Exception exception = null; 
            bool isToShowMessage = false; 
            try 
            { 
                InProgress = true; 
                var auth = await _sessionService.LoginAsync(provider); 
                if (!auth) 
                { 
                    await _messageBox.ShowAsync(AppResources.LoginView_LoginNotAllowed_Message, 
                        AppResources.MessageBox_Title, 
                        new List<string> 
                    { 
                        AppResources.Button_OK 
                    }); 
                } 
                else 
                { 
                    _navigationService.NavigateTo(new Uri(Constants.MainView, UriKind.Relative)); 
                } 
 
                InProgress = false; 
            } 
            catch (InvalidOperationException e) 
            { 
                InProgress = false; 
                isToShowMessage = true; 
            } 
            catch (Exception ex) 
            { 
                exception = ex; 
            } 
            if (isToShowMessage) 
            { 
                await _messageBox.ShowAsync(AppResources.LoginView_AuthFail, AppResources.ApplicationTitle, new List<string> { AppResources.Button_OK }); 
            } 
            if (exception != null) 
            { 
                await _logManager.LogAsync(exception); 
            } 
        } 
    }

Note: in LoginAction the parameter provider is the value of the CommandParameter received in LoginCommand, this is set in the login page.

The LoginPage.xaml is:

<phone:PhoneApplicationPage x:Class="AuthenticationSample.WP80.Views.LoginView" 
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                            xmlns:Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8" 
                            xmlns:controls="clr-namespace:Facebook.Client.Controls;assembly=Facebook.Client" 
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                            xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" 
                            xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" 
                            xmlns:converters="clr-namespace:Cimbalino.Phone.Toolkit.Converters;assembly=Cimbalino.Phone.Toolkit" 
                            Orientation="Portrait" 
                            SupportedOrientations="Portrait" 
                            shell:SystemTray.IsVisible="True" 
                            mc:Ignorable="d"> 
    <phone:PhoneApplicationPage.DataContext> 
        <Binding Mode="OneWay" 
                 Path="LoginViewModel" 
                 Source="{StaticResource Locator}" /> 
    </phone:PhoneApplicationPage.DataContext> 
    <phone:PhoneApplicationPage.Resources> 
        <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> 
    </phone:PhoneApplicationPage.Resources> 
    <phone:PhoneApplicationPage.FontFamily> 
        <StaticResource ResourceKey="PhoneFontFamilyNormal" /> 
    </phone:PhoneApplicationPage.FontFamily> 
    <phone:PhoneApplicationPage.FontSize> 
        <StaticResource ResourceKey="PhoneFontSizeNormal" /> 
    </phone:PhoneApplicationPage.FontSize> 
    <phone:PhoneApplicationPage.Foreground> 
        <StaticResource ResourceKey="PhoneForegroundBrush" /> 
    </phone:PhoneApplicationPage.Foreground> 
    <!--  LayoutRoot is the root grid where all page content is placed  --> 
    <Grid x:Name="LayoutRoot" Background="Transparent"> 
        <Grid.RowDefinitions> 
            <RowDefinition Height="Auto" /> 
            <RowDefinition Height="*" /> 
        </Grid.RowDefinitions> 
 
        <!--  TitlePanel contains the name of the application and page title  --> 
        <StackPanel x:Name="TitlePanel" 
                    Grid.Row="0" 
                    Margin="12,17,0,28"> 
            <TextBlock Margin="12,0" 
                       Style="{StaticResource PhoneTextNormalStyle}" 
                       Text="{Binding LocalizedResources.ApplicationTitle, 
                                      Mode=OneWay, 
                                      Source={StaticResource LocalizedStrings}}" /> 
            <TextBlock Margin="9,-7,0,0" 
                       Style="{StaticResource PhoneTextTitle1Style}" 
                       Text="{Binding LocalizedResources.LoginView_Title, 
                                      Mode=OneWay, 
                                      Source={StaticResource LocalizedStrings}}" /> 
        </StackPanel> 
 
        <!--  ContentPanel - place additional content here  --> 
        <Grid x:Name="ContentPanel" 
              Grid.Row="1" 
              Margin="24,0,0,-40"> 
            <Grid.RowDefinitions> 
                <RowDefinition Height="Auto" /> 
                <RowDefinition Height="Auto" /> 
                <RowDefinition Height="Auto" /> 
                <RowDefinition Height="Auto" /> 
                <RowDefinition Height="Auto" /> 
            </Grid.RowDefinitions> 
            <TextBlock Grid.Row="0" 
                       Style="{StaticResource PhoneTextTitle2Style}" 
                       Text="{Binding LocalizedResources.LoginView_UserAccount, 
                                      Mode=OneWay, 
                                      Source={StaticResource LocalizedStrings}}" /> 
            <Button Grid.Row="1" 
                    Margin="10" 
                    Command="{Binding LoginCommand}" 
                    CommandParameter="facebook" 
                    Content="Facebook" /> 
            <Button Grid.Row="2" 
                    Margin="10" 
                    Command="{Binding LoginCommand}" 
                    CommandParameter="microsoft" 
                    Content="Microsoft" /> 
            <Button Grid.Row="3" 
                    Margin="10" 
                    Command="{Binding LoginCommand}" 
                    CommandParameter="google" 
                    Content="Google" /> 
       </Grid> 
        <Grid Visibility="{Binding InProgress, Converter={StaticResource BooleanToVisibilityConverter}}" 
            Grid.Row="0" 
                   Grid.RowSpan="2"> 
            <Rectangle  
                   Fill="Black" 
                   Opacity="0.75" /> 
            <TextBlock  
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" 
                   Text="{Binding LocalizedResources.LoginView_AuthMessage, 
                                  Mode=OneWay, 
                                  Source={StaticResource LocalizedStrings}}" /> 
            <ProgressBar IsIndeterminate="True" IsEnabled="True" Margin="0,60,0,0"/> 
        </Grid> 
     
    </Grid> 
</phone:PhoneApplicationPage>

Login User Interface


Source Code Files

  • IFacebookService interface for FacebookService
  • IGoogleService interface for GoogleService
  • ILogManager interface for LogManager
  • IMicrosoftService interface for MicrosoftService
  • ISessionProvider interface for all providers interface (common methods)
  • ISessionService for SessionService
  • Session class for save the information about the session (provider, token, expired date)
  • FacebookService class that has all logic about the login / logout using Facebook provider
  • GoogleService class that has all logic about the login / logout using Google provider
  • MicrosoftService class that has all logic about the login / logout using Microsoft provider
  • SessionService class for manage login/logout (it will use all services provides described before)
  • LoginViewModel class for binding to LoginView.xaml
  • LoginView class that represent the page for login
  • MainViewModel class for binding to MainView.xaml
  • MainView class that appear after the login is ok
  • AboutViewModel class for binding to the AboutView.xaml
  • AboutView class that represents the about page
  • ViewModelLocator class contains static references to all the view model in  application and provides an entry point for the bindings.

Build the Sample

  1. Start Visual Studio Express 2012 for Windows 8 and select File > Open > Project/Solution.
  2. Go to the directory in which you unzipped the sample. Go to the directory named for the sample, and double-click the Visual Studio Express 2012 for Windows 8 Solution (.sln) file.
  3. Press F7 or use Build > Build Solution to build the sample.

 Note: you can use Visual Studio 2013 in Windows 8.1.

 Run the sample

To debug the app and then run it, press F5 or use Debug > Start Debugging. To run the app without   debugging, press Ctrl+F5 or use Debug > Start Without Debugging.

Related Samples

More Information

Ask me on twitter @saramgsilva

Cimbalino Windows Phone Toolkit Samples

This article has the goal to exposes all samples related with Cimbalino Toolkit

How to use Cimbalino Windows Phone Toolkit Background – ScreenInfoService

This samples has the goal to show how to use Cimbalino Windows Phone Toolkit Background – ScreenInfoService.

Introduction

Cimbalino Windows Phone Toolkit is a set of useful and powerful items that will help you build your Silverlight applications for Windows Phone. The Toolkit is divided in projects which deliver different features. The base project (Cimbalino.Phone.Toolkit) contains base MVVM services, some very useful converters, helper classes and extension methods, and the bindable Application Bar behaviour.

Cimbalino.Phone.Toolkit.Background is a MVVM compatible services for background agents.

Building Sample
This packages is available in Nuget Package Manager (for both targets) and can be installed, for more details see: How to install Cimbalino Windows Phone Toolkit packages.

The sample for this article uses MVVM Light for help in MVVM pattern implementation, for more details see:

Let’s start!

We should register each service in ViewModelLocator, as following:

  using Cimbalino.Phone.Toolkit.Services;
    using GalaSoft.MvvmLight.Ioc;
    using Microsoft.Practices.ServiceLocation;

    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// </summary>
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);


            if (!SimpleIoc.Default.IsRegistered<IScreenInfoService>())
            {
                SimpleIoc.Default.Register<IScreenInfoService, ScreenInfoService>();
            }

            SimpleIoc.Default.Register<MainViewModel>();
        }

        public MainViewModel MainViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }
        
        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }

Then we should implement the MainViewModel as following:

   /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// The _screen information service
        /// </summary>
        private readonly IScreenInfoService _screenInfoService;

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        /// <param name="screenInfoService">
        /// The screen Info Service.
        /// </param>
        public MainViewModel(IScreenInfoService screenInfoService)
        {
            _screenInfoService = screenInfoService;
        }

        public int ScaleFactor
        {
            get
            {
                return _screenInfoService.ScaleFactor;
            }
        }

        public string Resolution
        {
            get
            {
                if (_screenInfoService.Resolution == ScreenInfoServiceResolution.HD720p)
                {
                    return "HD720p";
                }
                if (_screenInfoService.Resolution == ScreenInfoServiceResolution.WVGA)
                {
                    return "WVGA";
                }
                if(_screenInfoService.Resolution==ScreenInfoServiceResolution.WXGA)
                {
                    return "WXGA";
                }
                return "Unknown";
            }
        }
    }

for connect view model with the page we should add the ViewModelLocator instance in App.xaml:

    <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"  />

and add the binding in main page like:

  DataContext="{Binding MainViewModel, 
                        Source={StaticResource Locator}}"

The MainPage.xaml can be the following:

<phone:PhoneApplicationPage x:Class="CimbalinoSample.MainPage"
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                            xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                            xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                            DataContext="{Binding MainViewModel,
                                                  Source={StaticResource Locator}}"
                            FontFamily="{StaticResource PhoneFontFamilyNormal}"
                            FontSize="{StaticResource PhoneFontSizeNormal}"
                            Foreground="{StaticResource PhoneForegroundBrush}"
                            Orientation="Portrait"
                            SupportedOrientations="Portrait"
                            shell:SystemTray.IsVisible="True"
                            mc:Ignorable="d">

    <!--  LayoutRoot is the root grid where all page content is placed  -->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <!--  TitlePanel contains the name of the application and page title  -->
        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="12,17,0,28">
            <TextBlock Margin="12,0"
                       Style="{StaticResource PhoneTextTitle2Style}"
                       Text="Cimbalino Sample" />
            <TextBlock Margin="9,-7,0,0"
                       Style="{StaticResource PhoneTextTitle2Style}"
                       Text="ScreenInfoService" />
        </StackPanel>

        <!--  ContentPanel - place additional content here  -->
        <Grid x:Name="ContentPanel"
              Grid.Row="1"
              Margin="12,0,12,0">
            <TextBlock>
                Scale Factor:<Run Text="{Binding ScaleFactor}" />
            </TextBlock>
            <TextBlock Margin="0,41,0,-41">
                Resolution:<Run Text="{Binding Resolution}" />
            </TextBlock>

        </Grid>

    </Grid>

</phone:PhoneApplicationPage>

How to use Cimbalino Windows Phone Toolkit Background – StorageService

This samples has the goal to show how to use Cimbalino Windows Phone Toolkit Background – StorageService.

Introduction

Cimbalino Windows Phone Toolkit is a set of useful and powerful items that will help you build your Silverlight applications for Windows Phone. The Toolkit is divided in projects which deliver different features. The base project (Cimbalino.Phone.Toolkit) contains base MVVM services, some very useful converters, helper classes and extension methods, and the bindable Application Bar behaviour.

Cimbalino.Phone.Toolkit.Background is a MVVM compatible services for background agents.

Building Sample
This packages is available in Nuget Package Manager (for both targets) and can be installed, for more details see: How to install Cimbalino Windows Phone Toolkit packages.

The sample for this article uses MVVM Light for help in MVVM pattern implementation, for more details see:

Let’s start!

We should register each service in ViewModelLocator, as following:

 using Cimbalino.Phone.Toolkit.Services;
    using GalaSoft.MvvmLight.Ioc;
    using Microsoft.Practices.ServiceLocation;

    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// </summary>
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            if (!SimpleIoc.Default.IsRegistered<IStorageService>())
            {
                SimpleIoc.Default.Register<IStorageService, StorageService>();
            }

            if (!SimpleIoc.Default.IsRegistered<ICameraCaptureService>())
            {
                SimpleIoc.Default.Register<ICameraCaptureService, CameraCaptureService>();
            }

            SimpleIoc.Default.Register<MainViewModel>();
        }

        public MainViewModel MainViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }

Then we should implement the MainViewModel as following:

using System.IO;
    using System.Threading.Tasks;
    using System.Windows.Input;
    using System.Windows.Media.Imaging;
    using Cimbalino.Phone.Toolkit.Extensions;
    using Cimbalino.Phone.Toolkit.Services;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;

    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// The storage service
        /// </summary>
        private readonly IStorageService _storageService;

        /// <summary>
        /// The camera capture service
        /// </summary>
        private readonly ICameraCaptureService _cameraCaptureService;

        /// <summary>
        /// The image source
        /// </summary>
        private BitmapImage _imageSource;

        /// <summary>
        /// The text
        /// </summary>
        private string _text;

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        /// <param name="storageService">The storage service.</param>
        /// <param name="cameraCaptureService">The camera capture service.</param>
        public MainViewModel(IStorageService storageService, ICameraCaptureService cameraCaptureService)
        {
            _storageService = storageService;
            _cameraCaptureService = cameraCaptureService;

            WriteTextCommand = new RelayCommand(WriteText);
            ReadTextCommand = new RelayCommand(ReadText);
            WriteImageCommand = new RelayCommand(WriteImage);
            ReadImageCommand = new RelayCommand(ReadImage);
        }

        /// <summary>
        /// Gets or sets the image source.
        /// </summary>
        /// <value>
        /// The image source.
        /// </value>
        public BitmapImage ImageSource
        {
            get { return _imageSource; }
            set { Set("ImageSource", ref _imageSource, value); }
        }

        /// <summary>
        /// Gets the read image command.
        /// </summary>
        /// <value>
        /// The read image command.
        /// </value>
        public ICommand ReadImageCommand { get; private set; }

        /// <summary>
        /// Gets the read text command.
        /// </summary>
        /// <value>
        /// The read text command.
        /// </value>
        public ICommand ReadTextCommand { get; private set; }

        /// <summary>
        /// Gets or sets the text.
        /// </summary>
        /// <value>
        /// The text.
        /// </value>
        public string Text
        {
            get { return _text; }
            set { Set("Text", ref _text, value); }
        }

        /// <summary>
        /// Gets the write image command.
        /// </summary>
        /// <value>
        /// The write image command.
        /// </value>
        public ICommand WriteImageCommand { get; private set; }

        /// <summary>
        /// Gets the show message box command.
        /// </summary>
        /// <value>
        /// The show message box command.
        /// </value>
        public ICommand WriteTextCommand { get; private set; }

        /// <summary>
        /// Converts to bitmap image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns></returns>
        public static BitmapImage ConvertToBitmapImage(byte[] image)
        {
            var bitmapImage = new BitmapImage();
            var memoryStream = new MemoryStream(image);
            bitmapImage.SetSource(memoryStream);
            return bitmapImage;
        }

        public void ReadImage()
        {
           var result = _storageService.ReadAllBytes("image.jpg");
            ImageSource = ConvertToBitmapImage(result);
        }

        /// <summary>
        /// Reads the text.
        /// </summary>
        /// <returns></returns>
        private void ReadText()
        {
            Text = _storageService.ReadAllText("Text.txt");
        }

        /// <summary>
        /// Results the image.
        /// </summary>
        /// <param name="obj">The object.</param>
        private void ResultImage(Microsoft.Phone.Tasks.PhotoResult obj)
        {
            _storageService.WriteAllBytes("image.jpg", obj.ChosenPhoto.ToArray());
        }

        /// <summary>
        /// Writes the image.
        /// </summary>
        private void WriteImage()
        {
            ImageSource = null;
            _cameraCaptureService.Show(ResultImage);
        }

        /// <summary>
        /// Writes the text.
        /// </summary>
        private void WriteText()
        {
            Text = string.Empty;
            _storageService.WriteAllText("Text.txt", "This is a text.");
        }
    }

for connect view model with the page we should add the ViewModelLocator instance in App.xaml:

    <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"  />

and add the binding in main page like:

  DataContext="{Binding MainViewModel, 
                        Source={StaticResource Locator}}"

The MainPage.xaml can be the following:

 

<phone:PhoneApplicationPage x:Class="CimbalinoSample.MainPage"
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                            xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                            xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                            DataContext="{Binding MainViewModel,
                                                  Source={StaticResource Locator}}"
                            FontFamily="{StaticResource PhoneFontFamilyNormal}"
                            FontSize="{StaticResource PhoneFontSizeNormal}"
                            Foreground="{StaticResource PhoneForegroundBrush}"
                            Orientation="Portrait"
                            SupportedOrientations="Portrait"
                            shell:SystemTray.IsVisible="True"
                            mc:Ignorable="d">

    <!--  LayoutRoot is the root grid where all page content is placed  -->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <!--  TitlePanel contains the name of the application and page title  -->
        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="12,17,0,28">
            <TextBlock Margin="12,0"
                       Style="{StaticResource PhoneTextTitle2Style}"
                       Text="Cimbalino Sample" />
            <TextBlock Margin="9,-7,0,0"
                       Style="{StaticResource PhoneTextTitle2Style}"
                       Text="StorageService" />
        </StackPanel>

        <!--  ContentPanel - place additional content here  -->
        <Grid x:Name="ContentPanel"
              Grid.Row="1"
              Margin="12,0,12,0">
            <Image Height="243"
                   VerticalAlignment="Bottom"
                   Source="{Binding ImageSource}"
                   Stretch="Uniform" />
            <Button Height="82"
                    VerticalAlignment="Top"
                    Command="{Binding WriteTextCommand}"
                    Content="Write text" />
            <Button Height="82"
                    Margin="0,82,0,0"
                    VerticalAlignment="Top"
                    Command="{Binding ReadTextCommand}"
                    Content="Read text" />
            <TextBlock Width="436"
                       Margin="10,182,0,424"
                       HorizontalAlignment="Left"
                       Text="{Binding Text}"
                       TextWrapping="Wrap" />
            <Button Margin="0,238,0,317"
                    Command="{Binding WriteImageCommand}"
                    Content="Write Image" />

            <Button Margin="0,312,0,243"
                    Command="{Binding ReadImageCommand}"
                    Content="Read Image" />

        </Grid>

    </Grid>

</phone:PhoneApplicationPage>