For one project I’m working on, I wanted to have some live tiles on the home page.

Unfortunately, I’ve not been able to find such control (at least for Windows 8) or, to be exact, I’ve not found a control that do exactly what I wanted. Indeed, after a quick search, I found a LiveTile control in Callisto, the Framework developed by Tim Heuer. But this control is, in my mind, not perfect because it works with IEnumerable/IList objects and display always the same template for each items. But in my case, I want to be able to display 2 differents templates (one for the front item and one for the back item).

So I’ve developed my own component using C#/XAML. This one might not be perfect but it’s a good start if you want to use it in your applications. Here is the C# code:

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;

namespace DemoLiveTileControl
{
    public class SlideEventArgs : EventArgs
    {
    }

    [TemplatePart(Name = FRONTITEM_PARTNAME, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = BACKITEM_PARTNAME, Type = typeof(ContentPresenter))]
    public sealed class LiveTile : Control
    {
        #region Constants

        const string FRONTITEM_PARTNAME = "PART_Front";
        const string BACKITEM_PARTNAME = "PART_Back";

        #endregion

        #region Enums

        public enum SlideDirection
        {
            Up,
            Left
        }

        #endregion

        #region Events

        public delegate void SlideEventHandler(object sender, SlideEventArgs args);

        public event SlideEventHandler SlideStarting;
        public event SlideEventHandler SlideEnded;

        #endregion

        #region Member Fields

        private DispatcherTimer _timer;
        private bool _isPlayingAnimation;
        private bool _isBackVisible;

        private ContentPresenter _frontItem;
        private ContentPresenter _backItem;

        private TranslateTransform _frontItemTransform;
        private TranslateTransform _backItemTransform;

        #endregion

        #region Dependency Properties

        public SlideDirection Direction
        {
            get { return (SlideDirection)GetValue(DirectionProperty); }
            set { SetValue(DirectionProperty, value); }
        }

        public static readonly DependencyProperty DirectionProperty =
            DependencyProperty.Register("Direction", typeof(SlideDirection), typeof(LiveTile), new PropertyMetadata(SlideDirection.Up));

        public object FrontContent
        {
            get { return (object)GetValue(FrontContentProperty); }
            set { SetValue(FrontContentProperty, value); }
        }

        public static readonly DependencyProperty FrontContentProperty =
            DependencyProperty.Register("FrontContent", typeof(object), typeof(LiveTile), new PropertyMetadata(null, (o, args) =>
            {
                var ctrl = o as LiveTile;
                if (ctrl != null && ctrl._frontItem != null)
                {
                    ctrl.ChangeDataContext();
                }
            }));

        public object BackContent
        {
            get { return (object)GetValue(BackContentProperty); }
            set { SetValue(BackContentProperty, value); }
        }

        public static readonly DependencyProperty BackContentProperty =
            DependencyProperty.Register("BackContent", typeof(object), typeof(LiveTile), new PropertyMetadata(null, (o, args) =>
            {
                var ctrl = o as LiveTile;
                if (ctrl != null && ctrl._backItem != null)
                {
                    ctrl.ChangeDataContext();
                }
            }));

        public DataTemplate FrontItemTemplate
        {
            get { return (DataTemplate)GetValue(FrontItemTemplateProperty); }
            set { SetValue(FrontItemTemplateProperty, value); }
        }

        public static readonly DependencyProperty FrontItemTemplateProperty =
            DependencyProperty.Register("FrontItemTemplate", typeof(DataTemplate), typeof(LiveTile), new PropertyMetadata(null, (o, args) =>
            {
                var ctrl = o as LiveTile;
                if (ctrl != null)
                {
                    ctrl.ChangeDataContext();
                }
            }));

        public DataTemplate BackItemTemplate
        {
            get { return (DataTemplate)GetValue(BackItemTemplateProperty); }
            set { SetValue(BackItemTemplateProperty, value); }
        }

        public static readonly DependencyProperty BackItemTemplateProperty =
            DependencyProperty.Register("BackItemTemplate", typeof(DataTemplate), typeof(LiveTile), new PropertyMetadata(null, (o, args) =>
            {
                var ctrl = o as LiveTile;
                if (ctrl != null)
                {
                    ctrl.ChangeDataContext();
                }
            }));

        private void ChangeDataContext()
        {
            if (this._frontItem != null && this._backItem != null)
            {
                this._frontItem.DataContext = this.FrontContent;
                this._backItem.DataContext = this.BackContent;
            }
        }

        #endregion

        #region Constructor

        public LiveTile()
        {
            this.DefaultStyleKey = typeof(LiveTile);

            this.SizeChanged += OnLiveTileSizeChanged;
            this.Unloaded += OnLiveTileUnLoaded;
        }

        #endregion

        protected override void OnApplyTemplate()
        {
            this._frontItem = this.GetTemplateChild(FRONTITEM_PARTNAME) as ContentPresenter;
            this._backItem = this.GetTemplateChild(BACKITEM_PARTNAME) as ContentPresenter;

            if(this._frontItem != null && this._backItem != null)
            {
                this._frontItemTransform = this._frontItem.RenderTransform as TranslateTransform;
                this._backItemTransform = this._backItem.RenderTransform as TranslateTransform;

                if (this._backItemTransform != null)
                {
                    if (this.Direction == SlideDirection.Up)
                    {
                        this._backItemTransform.Y = this.Height;
                    }
                    else
                    {
                        this._backItemTransform.X = this.Width;
                    }
                }

                var startTimer = new DispatcherTimer
                {
                    Interval = TimeSpan.FromSeconds(2)
                };
                startTimer.Tick += (sender, o) =>
                {
                    this.RaiseSlideStarting();

                    // Show Back item
                    this.ShowBackItem();

                    this._isBackVisible = !this._isBackVisible;

                    startTimer.Stop();
                    startTimer = null;
                };
                startTimer.Start();

                this.StartAnimation();
                this.ChangeDataContext();
            }

            base.OnApplyTemplate();
        }

        #region Private Methods

        private void OnLiveTileUnLoaded(object sender, RoutedEventArgs args)
        {
            if(this._timer != null)
            {
                this._timer.Stop();
                this._timer = null;
            }

            if(this._frontItemTransform != null)
            {
                if(this.Direction == SlideDirection.Up)
                {
                    this._frontItemTransform.Y = 0;
                }
                else
                {
                    this._frontItemTransform.X = 0;
                }
            }

            if (this._backItemTransform != null)
            {
                if (this.Direction == SlideDirection.Up)
                {
                    this._backItemTransform.Y = this.Height;
                }
                else
                {
                    this._backItemTransform.X = this.Width;
                }
            }
        }

        private void OnLiveTileSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if(this._frontItem != null && this._backItem != null)
            {
                this._frontItem.Width = this._backItem.Width = e.NewSize.Width;
                this._frontItem.Height = this._backItem.Height = e.NewSize.Height;
            }
        }

        private void RaiseSlideStarting()
        {
            var handler = this.SlideStarting;
            if (handler != null)
            {
                handler(this, new SlideEventArgs());
            }
        }

        private void RaiseSlideEnded()
        {
            var handler = this.SlideEnded;
            if (handler != null)
            {
                handler(this, new SlideEventArgs());
            }
        }

        private void StartAnimation()
        {
            if(this._timer == null)
            {
                this._timer = new DispatcherTimer
                {
                    Interval = new TimeSpan(0, 0, 6)
                };

                this._timer.Tick += (sender, o) =>
                {
                    if (!this._isPlayingAnimation)
                    {
                        this.RaiseSlideStarting();

                        if (this._isBackVisible)
                        {
                            // Show Front item
                            this.ShowFrontItem();
                        }
                        else
                        {
                            // Show Back item
                            this.ShowBackItem();
                        }

                        this._isBackVisible = !this._isBackVisible;
                    }
                };
                this._timer.Start();
            }
        }

        private void ShowFrontItem()
        {
            var sb = new Storyboard();

            var hideBackItemAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromMilliseconds(2000)),
                To = this.Direction == SlideDirection.Up ? this.Height : this.Width,
                FillBehavior = FillBehavior.HoldEnd,
                EasingFunction = new QuinticEase
                {
                    EasingMode = EasingMode.EaseOut
                }
            };

            Storyboard.SetTarget(hideBackItemAnimation, this._backItemTransform);
            Storyboard.SetTargetProperty(hideBackItemAnimation, this.Direction == SlideDirection.Up ? "Y" : "X");

            sb.Children.Add(hideBackItemAnimation);

            var showFrontItemAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromMilliseconds(2000)),
                To = 0,
                FillBehavior = FillBehavior.HoldEnd,
                EasingFunction = new QuinticEase
                {
                    EasingMode = EasingMode.EaseOut
                }
            };

            Storyboard.SetTarget(showFrontItemAnimation, this._frontItemTransform);
            Storyboard.SetTargetProperty(showFrontItemAnimation, this.Direction == SlideDirection.Up ? "Y" : "X");

            sb.Children.Add(showFrontItemAnimation);

            sb.Completed += (a, b) =>
            {
                this._isPlayingAnimation = false;

                this.RaiseSlideEnded();
            };
            sb.Begin();

            this._isPlayingAnimation = true;
        }

        private void ShowBackItem()
        {
            var sb = new Storyboard();

            var showBackItemAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromMilliseconds(2000)),
                To = 0,
                FillBehavior = FillBehavior.HoldEnd,
                EasingFunction = new QuinticEase
                {
                    EasingMode = EasingMode.EaseOut
                }
            };

            Storyboard.SetTarget(showBackItemAnimation, this._backItemTransform);
            Storyboard.SetTargetProperty(showBackItemAnimation, this.Direction == SlideDirection.Up ? "Y" : "X");

            sb.Children.Add(showBackItemAnimation);

            var hideFrontItemAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromMilliseconds(2000)),
                To = this.Direction == SlideDirection.Up ? -this.Height : -this.Width,
                FillBehavior = FillBehavior.HoldEnd,
                EasingFunction = new QuinticEase
                {
                    EasingMode = EasingMode.EaseOut
                }
            };

            Storyboard.SetTarget(hideFrontItemAnimation, this._frontItemTransform);
            Storyboard.SetTargetProperty(hideFrontItemAnimation, this.Direction == SlideDirection.Up ? "Y" : "X");

            sb.Children.Add(hideFrontItemAnimation);

            sb.Completed += (a, b) =>
                {
                    this._isPlayingAnimation = false;

                    this.RaiseSlideEnded();
                };
            sb.Begin();

            this._isPlayingAnimation = true;
        }

        #endregion
    }
}

And here is the XAML code:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DemoLiveTileControl">

    <Style TargetType="local:LiveTile">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:LiveTile">
                    <Grid Background="{TemplateBinding Background}"
                          Height="{TemplateBinding Height}"
                          Width="{TemplateBinding Width}">
                        <ContentPresenter x:Name="PART_Back"
                                          DataContext="{x:Null}"
                                          Content="{Binding}"
                                          ContentTemplate="{TemplateBinding BackItemTemplate}"
                                          RenderTransformOrigin="0.5,0.5">
                            <ContentPresenter.RenderTransform>
                                <TranslateTransform />
                            </ContentPresenter.RenderTransform>
                        </ContentPresenter>
                        <ContentPresenter x:Name="PART_Front"
                                          DataContext="{x:Null}"
                                          Content="{Binding}"
                                          ContentTemplate="{TemplateBinding FrontItemTemplate}"
                                          RenderTransformOrigin="0.5,0.5">
                            <ContentPresenter.RenderTransform>
                                <TranslateTransform />
                            </ContentPresenter.RenderTransform>
                        </ContentPresenter>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

As you can see, there are different properties that you can use but the most important are:

  1. Direction (Up or Left)
  2. FrontContent
  3. BackContent
  4. FrontItemTemplate
  5. BackItemTemplate

Here is a simple usage of the control:

<local:LiveTile FrontContent="Yoda"
                            BackContent="Dark Vador"
                            Direction="Up"
                            Height="245"
                            Width="220">
                <local:LiveTile.FrontItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <Image Source="/Assets/Yoda.jpg" Height="220" />
                            <TextBlock Text="{Binding}"
                                        Grid.Row="1"
                                       Foreground="White"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center" />
                        </Grid>
                    </DataTemplate>
                </local:LiveTile.FrontItemTemplate>
                <local:LiveTile.BackItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <Image Source="/Assets/DarkVador.png"
                                   Height="220" />
                            <TextBlock Text="{Binding}"
                                       Grid.Row="1"
                                       Foreground="White"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center" />
                        </Grid>
                    </DataTemplate>
                </local:LiveTile.BackItemTemplate>
            </local:LiveTile>

Using the control is really simple (you can even get  notified when the slide action will be performed or when it has ended). Here is a simple video of the control in action:

Of course, there might be some bugs so don’t hesitate to drop me a line !

 

Happy coding!

People tagging is a feature that can be found in a lot of applications that manipulate pictures (like Windows Live Photo Gallery, Picasa, etc.) but it’s also used on some well-know web applications like Facebook !

Today, we’ll see how to add this feature in your Windows Store application. The code will demonstrate how to tag people on pictures but also how to retrieve the persons tagged on a picture. So, if you plan to create an application that use/display photos, you may want to look at this article to add the people tagging feature.

Note: Just a quick note about the quality of the code. As you’ll see, a bit of C++ is required and I’m real beginner about C++ so please, forgive any mistakes/errors in C++ code and just remember that code is simple a POC that you could improve by yourself !

Introduction about People Tagging

People Tagging is a feature documented on MSDN under the WIC (Windows Imaging Component) part. Indeed, to be able to tag people on images, you’ll need to access metadata and this can be done only with WIC. In .NET, there is a class, called BitmapMetadata, which allows you to access bitmap metadata. Unfortunately, this class is not available in Windows 8 so we’ll need to use C++ to access WIC and expose the feature.

Microsoft has created a new schema, Extensible Metadata Platform (XMP) for tagging people within a digital image. This schema enables applications to store the names and locations of individuals who are in the image as metadata within the image. In addition to the new schema, the new photo property System.Photo.PeopleNames is available in Windows 7. This new property enables applications to read the individual’s names stored in the image metadata. WIC utilizes these new features by enabling applications to easily read and write people tagging metadata to digital photos.

With the name, developers can also retrieve another property, Rectangle, that define the coordinates corresponding to the people tagged in a picture.

Here is a simple representation of the XMP metadata for people tagging:

image

Implementation

To read and write metadata such as the new people tagging feature, we need to use the IWICMetadataQueryReader and IWICMetadataQueryWriter interfaces. These interfaces enable applications to use the metadata query language to write metadata to the individual frames of an image.

To add metadata, the simplest (but longer) way is to re-encode the image and add the metadata (so, in fact, you just create a copy of the image and add the new metadata). As WIC s not accessible in C# (except using some tools like SharpDX, an open-source project delivering the full DirectX API under the .NEt platform), we’ll see how to do that using C++:

void TagMeManager::AddPeopleNameToFile(Platform::String^ sourcePath, Platform::String^ copyPath, Platform::String^ peopleName, Platform::String^ peopleCoordinates)
{
    IWICImagingFactory *piFactory = NULL;
    IWICBitmapDecoder *piDecoder = NULL;

    // WIC Factory
    Tools::Check(
        CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&piFactory))
        );

    Tools::Check(piFactory->CreateDecoderFromFilename(
        sourcePath->Data(),
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnDemand, //For JPEG lossless decoding/encoding.
        &piDecoder)
        );

    // Variables used for encoding.
    IWICStream *piFileStream = NULL;
    IWICBitmapEncoder *piEncoder = NULL;
    IWICMetadataBlockWriter *piBlockWriter = NULL;
    IWICMetadataBlockReader *piBlockReader = NULL;

    WICPixelFormatGUID pixelFormat = { 0 };
    UINT count = 0;
    double dpiX, dpiY = 0.0;
    UINT width, height = 0;

    // Create a file stream.
    Tools::Check(piFactory->CreateStream(&piFileStream));

    // Initialize our new file stream.
    Tools::Check(piFileStream->InitializeFromFilename(
        copyPath->Data(),
        GENERIC_WRITE)
        );

    // Create the encoder.
    Tools::Check(piFactory->CreateEncoder(
        GUID_ContainerFormatJpeg,
        NULL,
        &piEncoder)
        );

    // Initialize the encoder
    Tools::Check(piEncoder->Initialize(piFileStream, WICBitmapEncoderNoCache));

    Tools::Check(piDecoder->GetFrameCount(&count));

    // Process each frame of the image.
    for (UINT i=0; i<count; i++)
    {
        // Frame variables.
        IWICBitmapFrameDecode *piFrameDecode = NULL;
        IWICBitmapFrameEncode *piFrameEncode = NULL;
        IWICMetadataQueryReader *piFrameQReader = NULL;
        IWICMetadataQueryWriter *piFrameQWriter = NULL;

        // Get and create the image frame.
        Tools::Check(piDecoder->GetFrame(
            i,
            &piFrameDecode)
            );

        Tools::Check(piEncoder->CreateNewFrame(
            &piFrameEncode,
            NULL)
            );

        // Initialize the encoder.
        Tools::Check(piFrameEncode->Initialize(
            NULL)
            );

        // Get and set the size.
        Tools::Check(piFrameDecode->GetSize(&width, &height));

        Tools::Check(piFrameEncode->SetSize(width, height));

        // Get and set the resolution.
        piFrameDecode->GetResolution(&dpiX, &dpiY);

        Tools::Check(piFrameEncode->SetResolution(dpiX, dpiY));

        // Set the pixel format.
        piFrameDecode->GetPixelFormat(&pixelFormat);

        Tools::Check(piFrameEncode->SetPixelFormat(&pixelFormat));

        // Check that the destination format and source formats are the same.
        bool formatsEqual = FALSE;
        GUID srcFormat;
        GUID destFormat;

        Tools::Check(piDecoder->GetContainerFormat(&srcFormat));
        Tools::Check(piEncoder->GetContainerFormat(&destFormat));

        if (srcFormat == destFormat)
            formatsEqual = true;
        else
            formatsEqual = false;

        if (formatsEqual)
        {
            // Copy metadata using metadata block reader/writer.
            piFrameDecode->QueryInterface(IID_PPV_ARGS(&piBlockReader));

            piFrameEncode->QueryInterface(IID_PPV_ARGS(&piBlockWriter));

            piBlockWriter->InitializeFromBlockReader(piBlockReader);
        }

        Tools::Check(piFrameEncode->GetMetadataQueryWriter(&piFrameQWriter));

        // Add additional metadata.
        /*PROPVARIANT padding;
        PropVariantInit(&padding);

        padding.vt = VT_UI4;
        padding.uintVal = 5012;

        Tools::Check(piFrameQWriter->SetMetadataByName(L"/xmp/PaddingSchema:Padding", &padding));
        Tools::Check(piFrameQWriter->SetMetadataByName(L"/app1/ifd/PaddingSchema:Padding", &padding));
        Tools::Check(piFrameQWriter->SetMetadataByName(L"/app1/ifd/exif/PaddingSchema:Padding", &padding));*/

        // Create XMP block
        IWICMetadataQueryWriter *pXMPriter = NULL;
        Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMP, NULL, &pXMPriter));

        PROPVARIANT xmpInfo;
        PropVariantInit(&xmpInfo);

        xmpInfo.vt = VT_UNKNOWN;
        xmpInfo.punkVal = pXMPriter;
        xmpInfo.punkVal->AddRef();

        piFrameQWriter->SetMetadataByName(L"/xmp", &xmpInfo);

        // Create XMP RegionInfos block
        IWICMetadataQueryWriter *pXMPRegionInfosWriter = NULL;
        Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMPStruct, NULL, &pXMPRegionInfosWriter));

        PROPVARIANT microsoftRegionInfo;
        PropVariantInit(&microsoftRegionInfo);

        microsoftRegionInfo.vt = VT_UNKNOWN;
        microsoftRegionInfo.punkVal = pXMPRegionInfosWriter;
        microsoftRegionInfo.punkVal->AddRef();

        piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo", &microsoftRegionInfo);

        IWICMetadataQueryWriter *pXMPRegionWriter = NULL;
        Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMPBag, NULL, &pXMPRegionWriter));

        PROPVARIANT microsoftRegions;
        PropVariantInit(&microsoftRegions);

        microsoftRegions.vt = VT_UNKNOWN;
        microsoftRegions.punkVal = pXMPRegionWriter;
        microsoftRegions.punkVal->AddRef();

        piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions", &microsoftRegions);

        IWICMetadataQueryWriter *pXMPStructWriter = NULL;
        Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMPStruct, NULL, &pXMPStructWriter));

        PROPVARIANT xmpstruct;
        PropVariantInit(&xmpstruct);

        xmpstruct.vt = VT_UNKNOWN;
        xmpstruct.punkVal = pXMPStructWriter;
        xmpstruct.punkVal->AddRef();

        piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}", &xmpstruct);

        // Convert Plateform::String^ to LPWSTR
        std::vector<wchar_t> nameBuffer(peopleName->Begin(), peopleName->End());
        nameBuffer.push_back(0);

        PROPVARIANT valueName;
        PropVariantInit(&valueName);

        valueName.vt = VT_LPWSTR;
        valueName.pwszVal = nameBuffer.data();

        Tools::Check(piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:PersonDisplayName", &valueName));

        // Convert Plateform::String^ to LPWSTR
        std::vector<wchar_t> coordinatesBuffer(peopleCoordinates->Begin(), peopleCoordinates->End());
        coordinatesBuffer.push_back(0);

        PROPVARIANT valueCoordinates;
        PropVariantInit(&valueCoordinates);

        valueCoordinates.vt = VT_LPWSTR;
        valueCoordinates.pwszVal = coordinatesBuffer.data();

        Tools::Check(piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:Rectangle", &valueCoordinates));

        Tools::Check(piFrameEncode->WriteSource(
            static_cast<IWICBitmapSource*> (piFrameDecode),
            NULL)); // Using NULL enables JPEG loss-less encoding.

        // Commit the frame.
        Tools::Check(piFrameEncode->Commit());

        // Release
        if(piFrameQWriter)
        {
            piFrameQWriter->Release();
        }

        if(piFrameQReader)
        {
            piFrameQReader->Release();
        }

        if(piFrameEncode)
        {
            piFrameEncode->Release();
        }

        if(piFrameDecode)
        {
            piFrameDecode->Release();
        }

        if(piDecoder)
        {
            piDecoder->Release();
        }

        if(piFactory)
        {
            piFactory->Release();
        }
    }

    piEncoder->Commit();
    piFileStream->Commit(STGC_DEFAULT);
    if (piFileStream)
    {
        piFileStream->Release();
    }
    if (piEncoder)
    {
        piEncoder->Release();
    }
    if (piBlockWriter)
    {
        piBlockWriter->Release();
    }
    if (piBlockReader)
    {
        piBlockReader->Release();
    }
}

Code is well documented but basically, we start by creating a copy of the file, we enumerate all the sources properties and apply them to the new image and finally, we add our custom metadata. This last part is the most important because, in my mind, not enough documented.

Previously, you see a representation of the XMP metadata for people tagging so the goal here is to re-create the same structure (if this one has not been created). Otherwise, you just need to update it:

// Create XMP block
IWICMetadataQueryWriter *pXMPriter = NULL;
Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMP, NULL, &pXMPriter));

PROPVARIANT xmpInfo;
PropVariantInit(&xmpInfo);

xmpInfo.vt = VT_UNKNOWN;
xmpInfo.punkVal = pXMPriter;
xmpInfo.punkVal->AddRef();

piFrameQWriter->SetMetadataByName(L"/xmp", &xmpInfo);

// Create XMP RegionInfos block
IWICMetadataQueryWriter *pXMPRegionInfosWriter = NULL;
Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMPStruct, NULL, &pXMPRegionInfosWriter));

PROPVARIANT microsoftRegionInfo;
PropVariantInit(&microsoftRegionInfo);

microsoftRegionInfo.vt = VT_UNKNOWN;
microsoftRegionInfo.punkVal = pXMPRegionInfosWriter;
microsoftRegionInfo.punkVal->AddRef();

piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo", &microsoftRegionInfo);

IWICMetadataQueryWriter *pXMPRegionWriter = NULL;
Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMPBag, NULL, &pXMPRegionWriter));

PROPVARIANT microsoftRegions;
PropVariantInit(&microsoftRegions);

microsoftRegions.vt = VT_UNKNOWN;
microsoftRegions.punkVal = pXMPRegionWriter;
microsoftRegions.punkVal->AddRef();

piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions", &microsoftRegions);

IWICMetadataQueryWriter *pXMPStructWriter = NULL;
Tools::Check(piFactory->CreateQueryWriter(GUID_MetadataFormatXMPStruct, NULL, &pXMPStructWriter));

PROPVARIANT xmpstruct;
PropVariantInit(&xmpstruct);

xmpstruct.vt = VT_UNKNOWN;
xmpstruct.punkVal = pXMPStructWriter;
xmpstruct.punkVal->AddRef();

piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}", &xmpstruct);

// Convert Plateform::String^ to LPWSTR
std::vector<wchar_t> nameBuffer(peopleName->Begin(), peopleName->End());
nameBuffer.push_back(0);

PROPVARIANT valueName;
PropVariantInit(&valueName);

valueName.vt = VT_LPWSTR;
valueName.pwszVal = nameBuffer.data();

Tools::Check(piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:PersonDisplayName", &valueName));

// Convert Plateform::String^ to LPWSTR
std::vector<wchar_t> coordinatesBuffer(peopleCoordinates->Begin(), peopleCoordinates->End());
coordinatesBuffer.push_back(0);

PROPVARIANT valueCoordinates;
PropVariantInit(&valueCoordinates);

valueCoordinates.vt = VT_LPWSTR;
valueCoordinates.pwszVal = coordinatesBuffer.data();

Tools::Check(piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:Rectangle", &valueCoordinates));

As you can see, we first start by created the XMPStruct (/xmp) then the XMPBag (/xmp/MP/RegionInfo). Then, another XMPStruct is created (/xmp/MP/RegionInfo/MPRI:Regions). Finally, for each person we want to tag, we add a region and put the name and rectangle of the (/xmp/MP/RegionInfo/MPRI:Regions/{ulong=0}/MPReg:PersonDisplayName).

Theses steps are very important because they allow you to create the basic metadata structure for people tagging. Without this, the code in the documentation won’t be able to work:

pQueryWriter->SetMetadataByName(
      L"/xmp/<xmpstruct>MP:RegionInfo/<xmpbag>MPRI:Regions/PersonDisplayName", &value);

In the previous code, we use the QureyWriter to set the metadata:

PROPVARIANT valueName;
PropVariantInit(&valueName);

valueName.vt = VT_LPWSTR;
valueName.pwszVal = nameBuffer.data();

Tools::Check(piFrameQWriter->SetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:PersonDisplayName", &valueName));

We set only the metadata for 1 people but you could easily modify the code to enumerate an array and use it.

Now that we’ve seen how to write metadata, we need to see how to read those metadata:

Platform::String^ TagMeManager::ReadPeopleNameFromFile(Platform::String^ filePath)
{
    IWICImagingFactory *piFactory = NULL;

    // WIC Factory
    Tools::Check(
        CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&piFactory))
        );

    // Create WIC Decoder from file
    Tools::Check(piFactory->CreateDecoderFromFilename(
        filePath->Data(),
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnDemand,
        &m_wicBitmapDecoder)
        );

    // Get frame from decoder
    Tools::Check(m_wicBitmapDecoder->GetFrame(
        0,  //JPEG has only one frame.
        &m_wicBitmapFrameDecode)
        );

    // Get metadata query reader
    Tools::Check(m_wicBitmapFrameDecode->GetMetadataQueryReader(
        &m_wicMetadataQueryReader)
        );

    Platform::String^ peopleName;

    PROPVARIANT personNameValue;
    PropVariantInit(&personNameValue);

    try
    {
        // Put code in try/catch because the property might not exist if the image is not tagged

        /*Tools::Check(m_wicMetadataQueryReader->GetMetadataByName(L"System.Photo.PeopleNames", &personNameValue));*/
        Tools::Check(m_wicMetadataQueryReader->GetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:PersonDisplayName", &personNameValue));

        peopleName = ref new Platform::String(personNameValue.pwszVal);
    }
    catch(Exception^ e)
    {
        // Do nothing
    }

    if(piFactory)
    {
        piFactory->Release();
    }

    return peopleName;
}

Platform::String^ TagMeManager::ReadPeopleRectangleFromFile(Platform::String^ filePath)
{
    IWICImagingFactory *piFactory = NULL;

    // WIC Factory
    Tools::Check(
        CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&piFactory))
        );

    // Create WIC Decoder from file
    Tools::Check(piFactory->CreateDecoderFromFilename(
        filePath->Data(),
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnDemand,
        &m_wicBitmapDecoder)
        );

    // Get frame from decoder
    Tools::Check(m_wicBitmapDecoder->GetFrame(
        0,  //JPEG has only one frame.
        &m_wicBitmapFrameDecode)
        );

    // Get metadata query reader
    Tools::Check(m_wicBitmapFrameDecode->GetMetadataQueryReader(
        &m_wicMetadataQueryReader)
        );

    Platform::String^ rectangle;

    PROPVARIANT rectangleValue;
    PropVariantInit(&rectangleValue);

    try
    {
        // Put code in try/catch because the property might not exist if the image is not tagged
        Tools::Check(m_wicMetadataQueryReader->GetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:Rectangle", &rectangleValue));

        rectangle = ref new Platform::String(rectangleValue.pwszVal);
    }
    catch(Exception^ e)
    {
        // Do nothing
    }

    if(piFactory)
    {
        piFactory->Release();
    }

    return rectangle;
}

Here again, the useful part of the code is the use of the QueryReader to get the metadata:

PROPVARIANT personNameValue;
PropVariantInit(&personNameValue);

try
    {
        // Put code in try/catch because the property might not exist if the image is not tagged
        Tools::Check(m_wicMetadataQueryReader->GetMetadataByName(L"/xmp/MP:RegionInfo/MPRI:Regions/{ulong=0}/MPReg:PersonDisplayName", &personNameValue));

        peopleName = ref new Platform::String(personNameValue.pwszVal);
    }
    catch(Exception^ e)
    {
        // Do nothing
    }

This code could be arrange to get the name and rectangle in one method (and also to get all the names/rectangles) but it gives you the first step to understand how it works.

Demonstration

Ok, our component for reading/writing people metadata is ready so let’s take a look at how to use it. Hopefully, thanks to WinRT projections, it’s really easy!

Here is a simple demo app that allows you to load an image.

If the image has people tagging metadata, we display the information on screen:

Screenshot (75)

If no metadata are set, we give users the possibility to define:

  1. The rectangle containing the person to tag
  2. The name of this person

Screenshot (77)

The C# code is really easy because the most complicated part is done in C++. Indeed, to read the metadata, we just need to use:

this.tbName.Text = _tagHelper.ReadPeopleNameFromFile(this._tempImage.Path);
this.tbCoordinates.Text = _tagHelper.ReadPeopleRectangleFromFile(this._tempImage.Path);

Similarly, the code to set the metadata is also very simple:

var borderLeft = Canvas.GetLeft(this.tagger) / this.preview.ActualWidth;
var borderTop = Canvas.GetTop(this.tagger) / this.preview.ActualHeight;
var borderWidth = this.tagger.ActualWidth / this.preview.ActualWidth;
var borderHeight = this.tagger.ActualHeight / this.preview.ActualHeight;

var coordinates = string.Format("{0},{1},{2},{3}", borderLeft, borderTop, borderWidth, borderHeight);

var copy = await this.GetCopyImage(this._tempImage);

this._tagHelper.AddPeopleNameToFile(this._tempImage.Path, copy, user, coordinates);

 

So as you can see, people tagging feature can be really cool and simple to implement in your app. Of course, the previous code is a simple prototype so if you want to grab it and improve it, feel free and let me know !

You can download the source code here.

 

Happy coding!

Microsoft has released recently Windows Azure Mobile Services, a set of tools allowing developers to get backend, authentication and notification for the Windows Store (and Windows Phone) applications.

Activate Windows Azure Mobile Services

The feature is currently in preview so to activate it, you need to go here and activate the feature:

image

For now, the feature is free but you need a valid Azure subscription to use it (thanks to my MSDN account, I’ve been able to set up my Azure account).

Create your first application

Once the feature has been activated, this one is available directly in your management portal. So go to https://manage.windowsazure.com and click on “Mobile Services” to see the list of all the services you’ve created:

image

Here, you can see that I’ve already created a mobile services but let’s take a look at how you can create your own. On the bottom of the screen, click on “New” and click on “Create”:

image

In the screen that appear, you’re asking to indicate the URL of your service (this one ends with “.azure-mobile.net”), the database that you want to use (a new one or a database you’ve already created) and the region. As the feature is still in preview, you’ll notice that there are only 2 regions available: West US and East US:

image

Don’t worry if you’re setting an URL that is already in use: the system will let you know that point:

image

Next, you’re asking the credentials to connect (or create) to the database:

image

Once this is done, you can click on your service and you’re redirected to the Dashboard page, where you can see some stats about usage:

image

image

Manage data

First thing you need to do is to create/add some tables to the database you’ve specified for your mobile service. For that, click on the “Data” tab and click on “Add a table” to create a new table:

image

On the screen that appear, you can create a new table and set the different permissions needed to perform select and CRUD operations:

image

There are 4 kind of permissions, each of them is explained here:

  1. Everyone: This means that any request for the operation against the table is accepted. This option leaves your data wide-open for everyone to access.
  2. Anybody with the Application Key: The application key is required to perform the operation. The application key is distributed with the application. Because this key is not securely distributed, it cannot be considered a security token. To secure access to your mobile service data, you must instead authenticate users before accessing.
  3. Only Authenticated Users: Only authenticated users are permitted to perform the operation. Scripts can be used to further restrict access to tables based on an authenticated user.
  4. Only Scripts and Admins: The operation requires the service master key, which limits the operation only to registered scripts or to administrator accounts.

You may notice that you’ve not specified the columns of the table. You can click on the table name to see its details but even here, on the “Columns” tab, you just see the columns, you’re not able to add them. By default, you don’t need to predefine the schema of tables in your database. Mobile Services automatically adds columns to a table based on the data you insert. To change this dynamic schema behavior, use the Dynamic Schema setting on the Configure tab:

image

Your data are ready, it’s now time to access them through your Windows Store application!

Access to your backend using Windows Store application

Before being able to access your data via Mobile Services, you need to install the Mobile Services SDK (available here). Once this is done, you can start by adding a reference to the SDK:

image

Then, you have to declare a variable, of type MobileServiceClient, that define a way to access to your Mobile Service:

image

The constructor takes 2 parameters:

  1. The URL from the Mobile Service. This one can be found on the left part of the Dashboard screen
  2. The ApplicationKey, which can be found if you click on the “Manage Keys” button:

image

Now, you need to define a class that represent the table you’ve created before. The technique here is quite similar to the one you can use if you use an ORM tool: you define a class for each table of the database. In our case, we need to define the Users class (be careful, the table already have an column named “id‘”):

image

You’re now ready to perform the basic operations . For example, if you want to query the table, just use the following code:

image

The method ToEnumerableAsync will convert the results to an enumerable object. You can also use the method ToCollectionView to get a CollectionView object from the results. The IMobileServiceTable<T> interface contains a lot of useful methods that you can use to get/insert/update/delete data from the Mobile Service:

image

So if you want to insert an item, you need to use the InsertAsync method:

image

Unfortunately, if you execute this code, you’ll encounter the following error:

image

Why are you unauthorized ? To understand this error, just remember the permissions we’ve set for the Users table:

image

So, as you can see, only scripts (server side components) and administrators are allowed to insert data. So how to make the difference between users and admins? Simply go the “Manage Keys” screen and look for the “Master Key”:

image

Previously, to create our MobileServiceClient object, we’ve used the Application Key but if we change it by the Master Key and re-execute the code, all will work fine and going to the table details screen will show you the item correctly added:

image

Here are some code samples to retrieve data:

image

First query will get all users where date of birth is previous to today. Second one use LookupAsync, taking in parameter the index of the item you want to retrieve.

So, as you can see, using Mobile Service is really simple and allow anyone to have a SQL Azure Database as a backend for any Windows Store application. But you can do more and that’s what we’ll see just now!

Execution of scripts during operations

If you go the details page of the table Users, you can see the “Scripts” tab:

image

On this page, you are able to add some JavaScript code that will occur when the selected operation is executed. Each script take some parameters:

  1. insert/update: Take in parameter the item that you want to insert/update, the user who submitted the query and the request, used to execute the operation and to notify the user by sending an response.
  2. del (delete): Take in parameter the id of the item to delete from the database
  3. read: Take in parameter the query that will be executed against the table, , the user who submitted the query and the request, used to execute the operation and to notify the user by sending an response.

For the request object, there are 2 methods that you can use:

  1. execute, that will execute the operation
  2. respond: that will report the response to the user. If you want to return an error to the user, you need to use this method, passing in parameters the error to displayed or the HTTP code and response to display to the user.

For your information, here is the full list of all the HTTP code available:

  1. OK: 200
  2. CREATED: 201
  3. ACCEPTED: 202
  4. NO_CONTENT: 204
  5. BAD_REQUEST: 400
  6. UNAUTHORIZED: 401
  7. FORBIDDEN: 403
  8. NOT_FOUND: 404
  9. CONFLICT: 409
  10. INTERNAL_SERVER_ERROR: 500

For example, if you want to perform a check (server side) of the data submitted, you can do it here:

image

Now, if you try to execute this code:

image

You’ll have the following error:

image

Complete document about server scripts can be found here: http://msdn.microsoft.com/en-US/library/windowsazure/jj554226.

Authenticate users in your Windows Store application

A common (and useful) scenario that need to be implemented in some application is the authentication of users. Thanks to Mobile Services, we can use the infrastructure provided by Microsoft and use it in our Windows Store application to allow connection through Live Services, Facebook, Twitter and Google.

First, you need to register your application on the Windows Store. Go to the Dev Center – Windows Store Apps and click on App name:

image

Type a name for your app in App name, click Reserve app name, and then click Save:

image

Now, in Visual Studio, right-click on your project in Solution Explorer click Store, and then click Associate App with the Store…:

image

Once connected, on the screen entitled “Associate Your App with the Windows Store”, select the app that you registered in step 2, click Next, and then click Associate:

image

image

Doing this steps allow you to update the application manifest to store the required information.

Now, go to the Live Connect Developer Center and, on My applications tab, and click your app in the My applications list:

image

Click Edit settings, then API Settings and make a note of the value of Client secret:

image

In Redirect domain, enter the URL of your mobile service (you can found it on the left your Dashboard) and click Save:

image

image

Go back to the Management Portal, click the Identity tab, enter the Client secret obtained from Windows Store, and click Save:

image

Your Mobile Service and your application are now configured to work with Live Connect: it’s time to add authentication to the app.

Start by download and install the Live SDK for Windows:

image

In the Project menu in Visual Studio, click Add Reference, then expand Windows, click Extensions, check Live SDK, and then click OK:

image

Once this is done, add the following code which help you to log on an user using Live Connect:

image

(Don’t forget to update the Redirect domain URL before to continue).

Finally, add this code that will update the first user of your database:

image

Unfortunately; if you execute the code, you’ll get another Unauthorized error:

image

Indeed, once again, the problem occurs because the Users table is configured to allow update only to authenticate users. So, before calling the UpdateUser method, add the following code which will authenticate the user:

image

Once the Authenticate method is called, the app display a screen asking confirmation to user (for security purpose):

image

After you’ve been successfully authenticated, the update method can be executed without any problems and entry is updated in database.

image

Send push notifications to your Windows Store application

Push notifications are another good technique to make your application feel more “Windows 8 compliant”. Using them with Mobile Services is really simple again. If you have done all previous tasks correctly, just go to Live Connect Developer Center and under API Settings, make a note of the values of Client secret and Package security identifier (SID):

image

In Windows Azure Management Portal, click Mobile Services, click your app and click the Push tab. Enter the Client secret and Package SID values obtained before, and then click Save:

image

Now, you’re ready to add push notifications to your app. First, we need to get a notification channel and store it in the database so add the following code in App.xaml.cs:

image

Then, at the top of the OnLaunched event handler in App.xaml.cs, add the following call to the new AcquirePushChannel method:

image

The channel will be store in the database and, thanks to the technique of dynamic schema, we just need to add the corresponding property to the Users object:

image

Now, just add a new item in the database, and don’t forget to set its Channel property:

image

Depending of the type of notification you want to use, you might need to edit the manifest file to specify if the application is capable to receive Toast notifications or not (in our case, it’s yes):

image

Code in application is finished but, as you can see, we don’t have written any code related to push notifications yet. This is because this part is done on the server side, using the scripts executed during the operations (read/write/delete/update).

Our previous “Insert” script was this one:

image

We’ll modify it to ask the server to send a push notification when the request is executed:

image

As you can see, when the request is executed, we send a Toast notification, the number 4 (which accept up to 3 lines of text). Of course, all others kind of notifications (I mean badge and tile) are available if you need/want them. Just save your script and execute your application: if you have performed all the steps correctly, you should received a notification:

image

Here is a more complex Insert script, which use the Table object to access the number of items in the Users table and push a notification using the value:

image

Executing this script show a new toast notification:

image

And values returns by the read method of the Table object is good:

image

Conclusion

As you can see, Windows Azure Mobile Services is really simple to use and provide a lot of great features. Advanced developers might regrets that they don’t have access to the code of the methods provided but I think it can really useful in some scenarios so don’t hesitate to take a look at it: I’m sure you’ll appreciate it!

 

Happy coding!

Now that you have finally developed your application, you may wonder how this one could help you to get money. There is, at least, 2 possibilities:

  1. Add some fee to get your application
  2. Create a free application that will have ads

The second option is interesting because it does not make sense to buy all applications, particularly if your application use a free service. So let’s take a look at the Microsoft’s solution to get some ads in your Windows 8 applications.

Download the SDK

First thing you need to do is to download the Microsoft Advertising SDK for Windows 8. This one can be found here. Once downloaded, install it and go to the next part (yes, this is probably the most simple part of this tutorial).

Register your application

To display ads in your Windows 8 application, you must first register your application with pubCenter. This allows pubCenter to communicate with your application through the pubCenter API and begin serving ads. So first, create an account to the pubCenter (https://pubcenter.microsoft.com) then:

  1. Click the Setup tab.
  2. Click Applications.
  3. Click Register application.
  4. In the Name box, enter an application name.
  5. In the Device type box, select the type of device (indeed, you can choose Windows 8 or Windows Phone because this is the same system).

image

Once an application is registered, you can begin creating ad units for your application. Don’t forget to note the Application ID that is display on the Application details screen: you’ll need it in your code:

image

Create Ad units

Ad units contain ads displayed in your application. You can create as many as ad units for your application. Here are the steps needed to create your first ad unit:

  1. Click the Setup tab.
  2. Click Ad units.
  3. Click the Windows 8 application tab.
  4. Click Create application ad unit.
  5. In the Ad unit name box, enter the name you want to use for this ad unit.
  6. In the Application box, select the application for this ad unit.
  7. In the Ad unit size box, select the dimensions of the ad unit as it will appear in your application. Note that, on the right part of the screen, you can see a sample preview.
  8. In the Windows 8 App Store categories section, choose one category for the type of advertisements.

image

In the Application code information section, make a note your Ad Unit ID. Once again, you’ll need it in your application code (note that the Application ID is recalled here too in case you miss it).

Use the Ads controls

Your application and ad unit are created, you are now ready to use them in your application code. First, as the controls will communicate with pubCenter using APIs, you need to be sure that Internet is allowed in your application. Double-click on the file Package.appxmanifest and, in Capabilities tab, look to see if Internet (Client) is checked.

image

OK, now, you need to add a reference to the Advertising SDK to be able to use the controls available to you. From the Solution Explorer window, right click References, and then select Add Reference…:

image

Click Windows, and that will expand to show Extensions. Click Extensions, select Microsoft Advertising SDK for Windows 8 (XAML), and then click OK:

image

If you are working on a JavaScript project, the steps are the same except that the reference is named Microsoft Advertising SDK for Windows 8 (JS):

image

Next steps are similar if you are working in XAML or JavaScript but, for simplicity purpose, I’ll cover both technology.

XAML Application

The AdControl can be used directly in your application. You need to add the following namespace before to continue:

xmlns:Ads="using:Microsoft.Advertising.WinRT.UI"

Then, just add the following code:

<Ads:AdControl ApplicationId="XXXXX-XXXX-XXXX-XXXX-XXXXXXX"
               AdUnitId="XXXXXX"
               Height="250"
               Width="250"
               HorizontalAlignment="Center"
               VerticalAlignment="Center" />

As you can see, you need to set the properties ApplicationId and AdUnitId (the ones you have to remember from the steps above) and, if you execute the code, you can see the following results:

image

The AdControl is just a simple ContentControl that use pubCenter APIs to get the ad that need to be displaed. If user click on the ad, he is redirected to the website dedicated to it.

JavaScript Application

The same result can be obtained from a JavaScript Application. After adding the reference, just add the following code:

<script src="/MSAdvertisingJS/ads/ad.js"></script>

Then, you’re now able to create an AdControl using the same technique you use to create a ListView (for example): create a div element and set its data-win-control property to MicrosoftNSJS.Advertising.AdControl:

<div id="adControl" style="position: absolute; top: 53px; left: 0px; width: 250px; height: 250px; z-index: 1"
     data-win-control="MicrosoftNSJS.Advertising.AdControl"
     data-win-options="{applicationId: 'XXXX-XXXX-XXXX-XXXX-XXXXXXX', adUnitId: 'XXXXXX'}">
</div>

Once again, you can see that properties applicationId and adUnitId are set (using the data-win-options property).

The result is quite similar to the XAML version (the image might not be the same because, remember, it’s provided by pubCenter):

image

 

As you can see, this is really easy to use and you can easily integrate it in your applications. For example, if you have a XAML application, just create a DataTemplateSelector:

public class ItemTemplateSelector : DataTemplateSelector
{
    protected override Windows.UI.Xaml.DataTemplate SelectTemplateCore(object item, Windows.UI.Xaml.DependencyObject container)
    {
        var sampleDataItem = item as SampleDataItem;
        if(sampleDataItem != null)
        {
            if(sampleDataItem.UniqueId == "pub")
            {
                return Application.Current.Resources["PubTemplate"] as DataTemplate;
            }
        }

        return Application.Current.Resources["Standard250x250ItemTemplate"] as DataTemplate;
    }
}

Using this code, I can choose the DataTemplate that my GridView will apply to its item.

<GridView x:Name="itemGridView"
          AutomationProperties.AutomationId="ItemGridView"
          AutomationProperties.Name="Grouped Items"
          Grid.RowSpan="2"
          Padding="116,137,40,46"
          ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
          ItemTemplateSelector="{StaticResource ItemTemplateSelector}"
          SelectionMode="None"
          IsSwipeEnabled="false"
          IsItemClickEnabled="True"
          ItemClick="ItemView_ItemClick">

Then, if I execute the code, you can see that all the items are correctly loaded as tiles but, for one of them, the tile display an ad:

image

Of course, the size for the tile of the ad is the same as all other tiles but if you create an Ad unit for different size, don’t forget to set up your GridView to use different items sizes.

More about AdControl

Here are some tips about the AdControl.

  1. First, if you want to refresh the ad displayed on screen, you need to set the property IsAutoRefreshEnabled to false. Then, call the Refresh method.
  2. If you want to improve performances when user is scrolling, set the property IsPerformanceScrollingEnabled to true. When the user will scroll, the current ad will temporarily be replaced by a snapshot image of the ad. When the AdControl has stopped scrolling it will revert back to normal behavior.
  3. Calling Suspend method, on the AdControl, suspends the current ad and replaces the current view of the ad with a snapshot of what was currently being rendered. After calling Suspend the AdControl will not automatically refresh, if IsAutoRefreshEnabled=true, and you cannot manually refresh the AdControl until Resume has been called.
  4. If you call the Resume method, it’ll remove the static snapshot of the AdControl and replaces it with the dynamic display.  If IsAutoRefreshEnable is set to true, automatic refresh will resume.

 

Results

To see how much money you’ve earned using the ads in your application, you just need to go in the pubCenter: https://pubcenter.microsoft.com/ReportManagement/Dashboard.aspx

image

Conclusion

As you can see, earning money with Windows 8 applications is really simple. Microsoft Advertising SDK simplify a lot the work for developers. You can set as many ads as you want in you application. You can even use multiple ads on the same screen but just be careful to not use too many ads or your users might don’t appreciate.

 

Happy coding!

[Windows 8] Using SQLite in your Windows Store application

October 10th, 2012 | Posted by Tom in .NET | Article | Windows 8 | WinJS | WinRT - (12 Comments) | Read: 32,576

SQLite is a small but very useful database engine widely deployed in the world. The folks on the team have been working hard to release a version compatible with Windows 8 and this one is available as an extension to Visual Studio.

Installation

To start using it, just go to the “Tools” menu and choose “Extensions and Update”:

image

Here, choose the “Online” section, search for “sqlite” then download and install “SQLite for Windows Runtime”:

image

Once Visual Studio restarted, just add the following reference to your project:

  1. SQLite for Windows Runtime
  2. Microsoft Visual C++ Runtime Package

image

Please note that to allow compilation of your project, this one cannot target the “Any CPU” configuration: you need to choose & build specific architecture (x86, x64 or ARM):

image

This means that, if you plan to release your application for any architecture, you’ll need to create (and submit) 3 different packages.

Finally, the last step you need to do is to grab a wrapper that will call the SQLite APIs. You can, for example, take sqlite-net (available on Nuget).

image

This will add 2 files to your solution: SQLite.cs (use to access the SQLite APIs) and SQLiteAsync.cs (the async version of the wrapper).

image

Now, you’re ready to use SQLite in your application! Documentation is very good so don’t hesitate to take a look at it but here are the basic steps you may want to perform.

Basic Actions

Access database

First thing you may want to do is simply to create a connection to the database. This can be perform by using the SQLiteConnection object (or its async version: SQLiteAsyncConnection), taking in parameter the path to the file which represent your database:

var path = Windows.Storage.ApplicationData.Current.LocalFolder.Path + @"\users.db";
var db = new SQLiteAsyncConnection(path);

Here is a quick snapshot of the methods available to the connection object (we’ll take a look at them further below):

image

Create table

Now that connection to the database is setup, we can start accessing data. First, we need to create some tables (at least one). This can be done by using one of the following (async) methods:

  1. CreateTableAsync:
  2. CreateTablesAsync:

Here is an example:

await db.CreateTableAsync<User>();

As you can see, we simply call the CreateTableAsync method, passing the table type as parameter (User in our case). So, as you might guess, this type must be defined somewhere:

[Table("Users")]
public class User
{
    [PrimaryKey, Unique]
    public Guid Id { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public int Age { get; set; }
}

Here, you can see some common attributes used with ORM tools like Table, PrimaryKey, Unique, etc. These attributes are used to configure tables when they will be created.

Note about using Guid type: In the code above, you can see that I’m using the Guid type. By default, this type is not supported in SQLite. You have to add the following compilation symbol to support Guid: SQLITE_SUPPORT_GUID

image

Insert data

To insert some data in the tables, you can add data one by one (using InsertAsync) or by using an IEnumerable object (using InsertAllAsync):

var tom = new User
{
    Id = Guid.NewGuid(),
    LastName = "LEBRUN",
    FirstName = "Thomas",
    Age = 30
};

await db.InsertAsync(tom);

var isUser = new List<User>()
{
    new User
    {
        Id = Guid.NewGuid(),
        LastName = "ANTOINE",
        FirstName = "Jonathan",
        Age = 18
     },
     new User
     {
         Id = Guid.NewGuid(),
         LastName = "SANTIN",
         FirstName = "Florent",
         Age = 29
     },
     new User
     {
         Id = Guid.NewGuid(),
         LastName = "MEZIL",
         FirstName = "Matthieu",
         Age = 30
     },
};

await db.InsertAllAsync(isUser);

Note that, as all methods are running asynchronously, I can use the await keyword (thanks to the async/await feature !).

Select data

Ok, now, we have our database with some tables and data inserted in the tables. But you’ll mostly want to query these tables to get their content. A possible method (we’ll see just after that there are other ways) is to use the QueryAsync method. This method take in parameter:

  1. The SQL string that you want to execute (SELECT * From Table, UPDATE …)
  2. The values of SQL parameters (if your query contains some of them)

Here is a example of a basic query that retrieve the total number of users in the database:

image

This query is a pretty simple one so let’s take a look at another one, a bit more complicated (e.g which use SQL parameters):

image

Here, you can see that each parameter of the query must be symbolized using the character “?”. Then, the value of the parameters is expressed as an object array in the second parameter of the method QueryAsync.

Update data

To update data in your database, you can use the method UpdateAsync. This method take as parameter the object (with its new properties updated) that you want to update.For example, look at the following code which get the first “old user”, update its age to 99 and query the database again to find this “very old guy”:

image

Except for the update method, the interesting part here is the use of the FindAsync method, which allow you to look for the first object corresponding to the predicate (this is more comfortable than using the SQL string syntax). For your information, you should know that there is another similar method that you can use: GetAsync.

Delete data

Deleting some data is as easy as getting or updating it. Indeed, the method DeleteAsync will do the job for you (as soon as you have retrieve the data you want to delete):

image

You may have noticed that I’ve not checked if the variable “veryOldUser” is not null before passing it in parameter to DeleteAsync. This is just for the demo and you should always perform this check before using your objects.

Drop table

Finally, you might need to drop a table from your database. To handle this scenario, simply use the method DropTableAsync:

image

Simple but useful!

Be careful now because if you tried to use a table that does not exist, you’ll encounter the following error:

image

Conclusions

As you can see, the APIs are very simple to use and performance is good. It could be very interesting to perform more performance tests but, except for the connection initialization which take a bit of time, I’ve not seen any performance issues.

Also, if you have some Windows Phone code using database in one of your application, you’ll be able to migrate it quite easily!

 

Feel free to contact me if you need more information!

 

Happy coding!

[Windows 8] How to access to the current user’s image ?

October 8th, 2012 | Posted by Tom in .NET | Article | Windows 8 | WinJS | WinRT - (1 Comments) | Read: 2,161

Lately, I was playing some game on my Windows Phone and notice something cool: the image displayed to represent my user was my current Windows account image.

I was wondering how I could perform the same thing in Windows 8, e.g how to access to the image of the current logged in user ?

After some tests, it really simple to do (and you can even get more information about the user):

displayName.Text = await UserInformation.GetDisplayNameAsync();
firstName.Text = await UserInformation.GetFirstNameAsync();
lastName.Text = await UserInformation.GetLastNameAsync();

var picture = UserInformation.GetAccountPicture(AccountPictureKind.LargeImage);
var stream = await picture.OpenAsync(FileAccessMode.Read);

var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);

img.Source = bitmapImage;

Using the UserInformation class, I’ve been able to access to the current user profile data like, for example, it’s first name, last name, display name, etc. But I can also use the GetAccountPicture method to access to the picture defined to my Windows account:

image

Really simple isn’t it ?!

Please note that, if you want to modify the image, you can use the following methods: SetAccountPictureAsync, SetAccountPictureFromStreamAsync, SetAccountPicturesAsync and SetAccountPicturesFromStreamAsync.

 

Happy coding!

[Windows 8] How to add “tilt effect” to your controls ?

September 18th, 2012 | Posted by Tom in .NET | Article | HTML5 | Windows 8 | WinJS | WinRT - (1 Comments) | Read: 2,959

The “tilt effect” was introduced in Windows Phone and give to developers the possibility to provide visual feedback to users by performing a small scale when the pointer is down on the control (and restoring the control to its original scale’s value when the pointer is up).

Here is a quick (but useful !) tip if you want to add this “tilt effect” to your controls:

(function () {
    "use strict";

    function addTiltEffect(control) {
        control.addEventListener('MSPointerDown', function () {
            WinJS.UI.Animation.pointerDown(control);
        }, false);

        control.addEventListener('MSPointerUp', function () {
            WinJS.UI.Animation.pointerUp(control);
        }, false);

        control.addEventListener('MSPointerOut', function () {
            WinJS.UI.Animation.pointerUp(control);
        }, false);
    }

    WinJS.Namespace.define("UiHelpers", {
        AddTiltEffect: addTiltEffect
    });
})();

As you can see, we just add handlers to the PointerDown, PointerUp and PointerOut events and we call the function pointerDown and pointerUp provided by the WinJS Framework !

 

Have fun and happy coding!

[Windows 8] How to take screenshots of a video?

August 30th, 2012 | Posted by Tom in .NET | Article | HTML5 | Windows 8 | WinJS | WinRT - (1 Comments) | Read: 1,351

Sometimes, it’s useful/needed to take screenshots of a video that is played by your application. Here is a simple (but working) technique to perform this task:

<section class="section">
    <p>
        <button id="btnVideoSelect">Select Video</button>
    </p>
    <p>
        <video id="videoPreview" width="350" height="250"></video>
    </p>
</section>

<section class="section2">
    <div id="outputListView" class="output" data-win-control="WinJS.UI.ListView"></div>
</section>
var screenshots = new WinJS.Binding.List();

var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.musicLibrary;
openPicker.fileTypeFilter.replaceAll([".mp4", ".mpeg", ".avi"]);
openPicker.pickSingleFileAsync().done(function(file) {
if (file) {
    var videoPreview = document.getElementById("videoPreview");
    if(videoPreview) {
        var fileLocation = window.URL.createObjectURL(file);

        videoPreview.src = fileLocation;
        videoPreview.setAttribute("controls", "true");

        var createScreenshots = false;

        videoPreview.addEventListener("ended", function() {
            createScreenshots = false;
        });

        videoPreview.addEventListener("play", function () {

            createScreenshots = true;

            setInterval(function () {
                if (createScreenshots) {
                    var canvas = document.createElement("canvas");
                    if (canvas) {

                         canvas.width = videoPreview.videoWidth;
                         canvas.height = videoPreview.videoHeight;

                         var ctx = canvas.getContext("2d");
                         ctx.drawImage(videoPreview, 0, 0, videoPreview.videoWidth, videoPreview.videoHeight);

                         var imageUrl = canvas.toDataURL();

                         screenshots.push({ url: imageUrl, width: 150, height: 50 });
                     }
                 }
             }, 500);
         });

            videoPreview.play();
        }
    }
});

Let’s take a look at the code now ! First, we use a FileOpenPicker to allow users to select the video to use. Once a video is selected, we set it a the source of the Video element. Then, as soon as the video starts to play, we create a Canvas element and access to the 2D context, that will allow us to call the drawImage method. This method accept, as a parameter, the video element that we use to play the video!

Finally, once the screenshot is generated, we use the method toDataURL to get its content encoded in Base64 and put it in a Binding list, which allows the ListView to be notified when new items are added !

As a result, take a look at this image!

image

This is just a prototype (code could be better) but you get the main idea! Of course, you could also use this code to get the screenshot at a particular time of the video (like if you wanted to extract a particular frame from the video).

 

Happy coding!

[Windows 8] How to download files from FTP ?

August 17th, 2012 | Posted by Tom in .NET | Article | Windows 8 | WinJS | WinRT - (1 Comments) | Read: 5,679

Downloading files is a common scenario, specially in connected apps like you can find in Windows 8.

When these files are hosted in Web server, downloads is really easy to perform using a WebRequest (C#) or XHR (XmlHttpRequest, in JS). But when the files are located on FTP server, it’s a bit more complicated so we have to find a way to be able to download files.

The answer to this problem is to use the BackgroundDownloader class:

var file = await KnownFolders.DocumentsLibrary.CreateFileAsync("Yoda.jpg", CreationCollisionOption.ReplaceExisting);

var pc = new PasswordCredential();
pc.UserName = "freebox";
pc.Password = "XXXXX";

var downloader = new BackgroundDownloader();
downloader.ServerCredential = pc;
var downloadOperation = downloader.CreateDownload(new Uri("ftp://hd1.freebox.fr/Disque dur/Yoda.jpg"), file);

var progressCallback = new Progress<DownloadOperation>(op =>
{
    if (op.Progress.TotalBytesToReceive > 0)
    {
        Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            this._progressPercent = op.Progress.BytesReceived * 100 / op.Progress.TotalBytesToReceive;
        });

        }
});

await downloadOperation.StartAsync().AsTask(progressCallback);

var response = downloadOperation.GetResponseInformation();

Unfortunately, if you try to execute the following code, you’ll encounter this error:

image

Indeed, there is actually a bug when you try to specify the ServerCredential property of the BackgroundDownloader (thanks to Katrien De Graeve, @katriendg for pointing me this). The workaround is to set the username/password in the Uri:

var file = await KnownFolders.DocumentsLibrary.CreateFileAsync("Yoda.jpg", CreationCollisionOption.ReplaceExisting);

var pc = new PasswordCredential();
pc.UserName = "freebox";
pc.Password = "XXXXX";

var downloader = new BackgroundDownloader();
//downloader.ServerCredential = pc;
var downloadOperation = downloader.CreateDownload(new Uri("ftp://freebox:XXXXX@hd1.freebox.fr/Disque dur/Yoda.jpg"), file);

var progressCallback = new Progress<DownloadOperation>(op =>
{
    if (op.Progress.TotalBytesToReceive > 0)
    {
        Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            this._progressPercent = op.Progress.BytesReceived * 100 / op.Progress.TotalBytesToReceive;
        });

    }
});

await downloadOperation.StartAsync().AsTask(progressCallback);

var response = downloadOperation.GetResponseInformation();

Using this code, you’ll be able to download the file successfully !

Note that, to use this code, you need to set the following capacities:

image

Happy coding!

[Windows 8] How to get the user’s current culture ?

July 26th, 2012 | Posted by Tom in .NET | Article | HTML5 | Windows 8 | WinJS | WinRT - (1 Comments) | Read: 2,687

In .NET application, to retrieve the user’s current culture is pretty simple using the CultureInfo class. But performing the same thing, in particular using Javascript, is a bit more complex.

So here is a quick code sample to perform this task:

image

As you can see, we just need to access to the languages property of the GlobalizationPreferences class. This property is an array containing all the cultures installed and used on the machine.

Here is the results of the previous call:

image

Simple but very useful ! To see more content about localization, check here: http://msdn.microsoft.com/en-us/library/windows/apps/hh465006.aspx

 

Happy coding!