Customise Scroll Bar in #uwp

Controls like ListView / GridView contain a ScrollViewer which hosts scrollable content. The ScrollViewer contains two ScrollBar controls one for each scroll type (horizontal and vertical).

Getting to a control’s template can be painful.. why not just head to
Default control styles and templates and download the templates you need

If you want to do it the hard way..
You can get to control’s ScrollViewer by right clicking on it and click Edit Template > Edit a Copy
Screenshot (39)

Looking at the template you can see the ScrollViewer but there’s little you can do right now.

<ControlTemplate TargetType="ListView">
    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
        <ScrollViewer x:Name="ScrollViewer" AutomationProperties.AccessibilityView="Raw" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}" IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" TabNavigation="{TemplateBinding TabNavigation}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
            <ItemsPresenter FooterTransitions="{TemplateBinding FooterTransitions}" FooterTemplate="{TemplateBinding FooterTemplate}" Footer="{TemplateBinding Footer}" HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}" Padding="{TemplateBinding Padding}"/>
        </ScrollViewer>
    </Border>
</ControlTemplate>

You need to go and Edit the template for ScrollViewer next.
You can right click on the ListView, tap Esc key which then moves focus on ScrollViewer. Click control’s Border and right click > Edit Template > Edit a copy

<ControlTemplate TargetType="ScrollViewer">
    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
        <Grid Background="{TemplateBinding Background}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ScrollContentPresenter x:Name="ScrollContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding ContentTemplate}" Margin="{TemplateBinding Padding}" Grid.RowSpan="2"/>
            <ScrollBar x:Name="VerticalScrollBar" Grid.Column="1" HorizontalAlignment="Right" IsTabStop="False" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{TemplateBinding VerticalOffset}" ViewportSize="{TemplateBinding ViewportHeight}"/>
            <ScrollBar x:Name="HorizontalScrollBar" IsTabStop="False" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{TemplateBinding HorizontalOffset}" ViewportSize="{TemplateBinding ViewportWidth}"/>
            <Border x:Name="ScrollBarSeparator" Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}" Grid.Column="1" Grid.Row="1"/>
        </Grid>
    </Border>
</ControlTemplate>

In one instance I needed the ScrollBar to not render over the content. You can see that the ScrollViewer ScrollViewerPresenter has both ColSpan and RowSpan set. Just get rid of it so there is no overlay.
In Another instance I needed to customise it further. I needed Horizontal ScrollBar (to be of same height as the ListView) and I needed only the Left and Right buttons. So start by Setting ColSpan / RowSpan on ScrollBar as needed
Now lets get to ScrollBar template to customise it further.

<ControlTemplate x:Key="HorizontalIncrementTemplate" TargetType="RepeatButton">
    <Grid x:Name="Root">          
        <Grid x:Name="HorizontalRoot" IsHitTestVisible="False">
            <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Rectangle x:Name="HorizontalTrackRect"
                        Grid.ColumnSpan="5"
                        Margin="0"
                        StrokeThickness="{ThemeResource ScrollBarTrackBorderThemeThickness}"
                        Fill="{ThemeResource SystemControlPageBackgroundChromeLowBrush}"
                        Stroke="{ThemeResource SystemControlForegroundTransparentBrush}" />
            <RepeatButton x:Name="HorizontalSmallDecrease"
                        Grid.Column="0"
                        MinHeight="12"
                        IsTabStop="False"
                        Interval="50"
                        Margin="0"
                        Template="{StaticResource HorizontalDecrementTemplate}"
                        Width="12"
                        VerticalAlignment="Center" />
            <RepeatButton x:Name="HorizontalLargeDecrease"
                        Grid.Column="1"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        IsTabStop="False"
                        Interval="50"
                        Template="{StaticResource RepeatButtonTemplate}"
                        Width="0" />
            <Thumb x:Name="HorizontalThumb"
                        Grid.Column="2"
                        Background="{ThemeResource SystemControlForegroundChromeHighBrush}"
                        Template="{StaticResource HorizontalThumbTemplate}"
                        Height="12"
                        MinWidth="12"
                        AutomationProperties.AccessibilityView="Raw" />
            <RepeatButton x:Name="HorizontalLargeIncrease"
                        Grid.Column="3"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        IsTabStop="False"
                        Interval="50"
                        Template="{StaticResource RepeatButtonTemplate}" />
            <RepeatButton x:Name="HorizontalSmallIncrease"
                        Grid.Column="4"
                        MinHeight="12"
                        IsTabStop="False"
                        Interval="50"
                        Margin="0"
                        Template="{StaticResource HorizontalIncrementTemplate}"
                        Width="12"
                        VerticalAlignment="Center" />
        </Grid>
        <Grid x:Name="HorizontalPanningRoot" MinWidth="24">
            <Border x:Name="HorizontalPanningThumb"
                    VerticalAlignment="Bottom"
                    HorizontalAlignment="Left"
                    Background="{ThemeResource SystemControlForegroundChromeDisabledLowBrush}"
                    BorderThickness="0"
                    Height="2"
                    MinWidth="32"
                    Margin="0,2,0,2"/>
        </Grid>
        <Grid x:Name="VerticalRoot" IsHitTestVisible="False">
            <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Rectangle x:Name="VerticalTrackRect"
                        Grid.RowSpan="5"
                        Margin="0"
                        StrokeThickness="{ThemeResource ScrollBarTrackBorderThemeThickness}"
                        Fill="{ThemeResource SystemControlPageBackgroundChromeLowBrush}"
                        Stroke="{ThemeResource SystemControlForegroundTransparentBrush}" />
            <RepeatButton x:Name="VerticalSmallDecrease"
                        Height="12"
                        MinWidth="12"
                        IsTabStop="False"
                        Interval="50"
                        Margin="0"
                        Grid.Row="0"
                        Template="{StaticResource VerticalDecrementTemplate}"
                        HorizontalAlignment="Center" />
            <RepeatButton x:Name="VerticalLargeDecrease"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Height="0"
                        IsTabStop="False"
                        Interval="50"
                        Grid.Row="1"
                        Template="{StaticResource RepeatButtonTemplate}" />
            <Thumb x:Name="VerticalThumb"
                        Grid.Row="2"
                        Background="{ThemeResource SystemControlForegroundChromeHighBrush}"
                        Template="{StaticResource VerticalThumbTemplate}"
                        Width="12"
                        MinHeight="12"
                        AutomationProperties.AccessibilityView="Raw" />
            <RepeatButton x:Name="VerticalLargeIncrease"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        IsTabStop="False"
                        Interval="50"
                        Grid.Row="3"
                        Template="{StaticResource RepeatButtonTemplate}" />
            <RepeatButton x:Name="VerticalSmallIncrease"
                        Height="12"
                        MinWidth="12"
                        IsTabStop="False"
                        Interval="50"
                        Margin="0"
                        Grid.Row="4"
                        Template="{StaticResource VerticalIncrementTemplate}"
                        HorizontalAlignment="Center" />
        </Grid>
        <Grid x:Name="VerticalPanningRoot" MinHeight="24">
            <Border x:Name="VerticalPanningThumb"
                    VerticalAlignment="Top"
                    HorizontalAlignment="Right"
                    Background="{ThemeResource SystemControlForegroundChromeDisabledLowBrush}"
                    BorderThickness="0"
                    Width="2"
                    MinHeight="32"
                    Margin="2,0,2,0"/>
        </Grid>
    </Grid>
</ControlTemplate>

In my case I only needed the two RepeatButton named HorizontalSmallDecrease and HorizontalSmallIncrease. Comment out the remaining controls (VerticalTrackRect, VerticalLargeDecrease, VerticalThumb, VerticalLargeIncrease)
For smoother scroll, modify RepeatButton’s Interval property, I set it to 5 rather than 50.

For further customisation I even created Attached Property that resizes the RepeatButton (the visibility is controlled by the ScrollBar so setting width while a hack works well.

public static class ScrollBarHelper
{
    static double InitialWidth = Double.MinValue;

    public static readonly DependencyProperty DCustomiseScrollBehaviourProperty =
    DependencyProperty.RegisterAttached("CustomiseScrollBehaviour", typeof(bool),
    typeof(ScrollBarHelper), new PropertyMetadata(false, OnCustomiseScrollBehaviourPropertyChanged));

    public static void SetCustomiseScrollBehaviour(DependencyObject d, bool value)
    {
        d.SetValue(DCustomiseScrollBehaviourProperty, value);
    }

    public static bool GetCustomiseScrollBehaviour(DependencyObject d)
    {
        return (bool)d.GetValue(DCustomiseScrollBehaviourProperty);
    }

    private static void OnCustomiseScrollBehaviourPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        if (!(bool)e.NewValue)
            return;

        var control = d as ScrollBar;
        if (control != null)
        {
            Observable.FromEventPattern<RoutedEventArgs>(control, "Loaded")
                .SubscribeOn(TaskPoolScheduler.Default)
                .ObserveOn(CoreDispatcherScheduler.Current)
                .Subscribe(args =>
                {
                    ProcessPosition(args.Sender);
                });

            Observable.FromEventPattern<ScrollEventArgs>(control, "Scroll")
                .Throttle(TimeSpan.FromMilliseconds(50))
                .SubscribeOn(TaskPoolScheduler.Default)
                .ObserveOn(CoreDispatcherScheduler.Current)
                .Subscribe(args =>
                {
                    ProcessPosition(args.Sender);
                });
        }
    }

    private static void ProcessPosition(object sender)
    {
        ScrollBar sb = (sender as ScrollBar);

        RepeatButton rbLeft = (RepeatButton)sb.FindDescendantByName("HorizontalSmallDecrease");
        RepeatButton rbRight = (RepeatButton)sb.FindDescendantByName("HorizontalSmallIncrease");

        if (rbLeft == null || rbRight == null)
            return;

        double pos = sb.Value;

        if (pos == 0 && InitialWidth == Double.MinValue)
        {
            InitialWidth = rbLeft.Width;
        }

        if (Math.Abs(pos - sb.Minimum) <= 15)
        {
            // hide the left scroll button
            rbLeft.Width = 0;
        }
        else if (Math.Abs(pos - sb.Maximum) <= 15)
        {
            // hide right scroll button
            rbRight.Width = 0;
        }
        else
        {
            rbLeft.Width = rbRight.Width = InitialWidth;
        }
    }
}

You could directly attached to ScrollBar’s Loaded / Scroll events but I used Reactive Extensions to throttle scroll changes as I wanted scroll experience to not degrade. The result.. not perfect but close to what I needed

Coding4Fun v2.0.9 released #wpdev #windev #winrt

Coding4Fun toolkit v2.0.9 for Windows Platform dev has been released and packages are available for download from Nuget.

This update builds additional support for Windows Runtime on Windows Phone 8.1 and Windows 8.1. Controls added to this release include

  • MetroFlow control (Windows 8.1 and WP 8.1)
  • Prompts (Toast, User, Message, Input, PasswordInput) for WP 8.1
  • BrushToBrushConverter now allows use of parameter to set output Opacity.

AppBarPrompt requires additional work the port wasn’t successful first time around. For issues there’s codeplex.

Happy coding

WinRT and application view wide Pointer event monitoring #wpdev #windev

It seems like every year I go through same technical difficulties. Last year I blogged about WinRT and pointer manipulation #win8dev #winrtdev

One of the underlying classes detects manipulation by user by means of Touch class. This class never made it to WinRT XAML and I have been messing around for a few days now… until something finally sunk in..

CoreWindow exposes following events

PointerEntered Occurs when a pointer moves into the bounding box of the Windows Store app.
PointerExited Occurs when the pointer moves outside the bounding box of the Windows Store app.
PointerMoved Occurs when a pointer moves within the bounding box of the Windows Store app.
PointerPressed Occurs when a mouse button is clicked, or a touch or pen contact is detected, within the bounding rectangle of the Windows Store app.
PointerReleased Occurs when a mouse button is released, or a touch or pen contact is lifted, within the bounding rectangle of the Windows Store app.

These events work exactly like Touch.TouchFrameReported and get bubbled up no matter which UIElement is tapped.

Time to continue porting

Support for Windows Phone 8.1 WinRT for Coding4Fun

Earlier this year, I took the responsibility of ensuring that  Coding4Fun toolkit supported Windows Runtime apps on Windows Phone 8.1. I spent a good part of March / April porting major chunks of code without porting XAML.

Occasionally I was reminded of the pending work by both Clint Rutkas and others, namely Glenn Versweyveld. After another round this week, I decided to take advantage of the quiet time I am having to push this through.

I have created a fork of coding4fun repo on codeplex and so far this is what i have done

  • Coding4Fun.Toolkit updated to support universal apps (Win8.1 and WP8.1)
  • Coding4Fun.Toolkit.Audio updated to support universal apps (Win8.1 and WP8.1)
  • Coding4Fun.Toolkit.Storage updated to support universal apps.
  • Created Coding4Fun.Toolkit.Controls project to explicitly support WP8.1
  • Added Tile, ImageTile, SuperImage, ColorHexagonPicker, BubbleChat & BubbleChatTextBox controls

Once parity with existing Win8.1 lib has been achieved, the next version of toolkit will be released on Nuget.

WindowsPhone 8.1, NavigationBar and Silverlight apps #wpdev

With Windows Phone 8.1 Microsoft drastically changed on of the basic chassis requirements in Windows Phone since day 0. The hardware buttons at the bottom of the phone. There were many reasons and one of them was the ability to use same chassis for both Android and Windows Phone devices. This many cited was very important smaller OEMs that recently signed up to Windows Phone ecosystem.

WP7 resolutions:

  • 800 x 480

WP8 resolutions (and scaling factors as reported  by Silverlight apps):

  • 800 x 480     (1.0)
  • 1280 x 720   (1.5)
  • 1280 x 768   (1.6)
  • 1920 x 1080 (2.25)

For Silverlight Windows Phone app (most of the apps that is) the resolution defaults to 800 x 480. This fits nicely with Scaling factor 1.6 which was used by many early Nokia devices. The scaling factor 1.5 however gave 853 x 480. This primarily affected HTC 8 S/X devices and Samsung ATIV S devices only. Later when Lumia 1520 came out, it uses 1020 and that used 2.25 and that also had 853 x 480 res.

What this meant was that WP7 and any WP8 apps that fix their resolution would leave 53 px gap. With my 1Shot camera app, I have a complicated control structure and I ended up fixing many heights and widths.. Not only that I also move certain objects on OrientationChanged using specific values of TranslateX and TranslateY. Last week after a user complaint, I added code to detect 720p and 1080p resolutions and to stretch those by additional 53px. However I never tested this on newer 720p devices that no longer have hardware buttons.

What Windows Phone Dev team did was added another system control like ApplicationBar called NavigationBar. Unlike ApplicationBar, NavigationBar isn’t accessible from within application. Playing with emulator set to show NavigationBar did not show any change in size reported by

double width = Application.Current.Host.Content.ActualWidth;
double height = Application.Current.Host.Content.ActualHeight;

After much complaining around twitter I came across windows phone 8.1 hide NavigationBar post on MSDN Forums. One of the suggestions was using PhoneApplicationFrame.FullScreen. Setting it to true or false made no difference.. none at all. After another round of complains, I came across this post Layout and the Windows Phone navigation bar.. it suggest that SizeChanged event should take care of it and ActualWidth and ActualHeight properties will sort it out.

Being not particularly clever, I thought that Page’s SizeChanged would be fired.. sure it was firing but ActualWidth and ActualHeight were 0.0. Width and Height were NaN so that was useless. Thinking that I could do better than that, I tried using SizeChanged exposed by Application.Current.Host.Content.. nope no difference.. it wasn’t a particularly bright idea.. Yesterday I spent time deliberating whether I should use hardcoded lists to indicate NavigationBar availability or not.. I already do so for high resolution sensors.. nah too much hassle

So today I said.. surely there must be something… on SizeChanged event handler of page, I scanned App.RootFrame.. I found something.. a private field called _visibleRegion correctly showed that 53 px at the botton were not available on 720p emulator when NavigationBar was visible. I think I am clever than most…

private Thickness GetMargin()
{
    Thickness t = new Thickness();

    var field = App.RootFrame.GetType().GetField("_visibleRegion", BindingFlags.Instance | BindingFlags.NonPublic);

    if (field != null)
    {
        object visibleRegion = field.GetValue(App.RootFrame);

        if (visibleRegion != null && visibleRegion is Thickness)
        {
            t = (Thickness)visibleRegion;
        }
    }

    return t;
}

This would get me the right margin and I could just just correct a few bits and bingo.. hahah yah right.. FieldAccessExpcetion eat that you clever git.. Apps don’t run under full trust on phone and I couldn’t reflect to this level. Sigh.. This was hard.. why had Windows Phone team gone way out of their way to piss developers off ?? Having almost lost all hope, I thought, let me try subscribe to Page’s LayoutRoot grid’s SizeChanged event… had a look there.. event handler exposed NewValue and they showed the correct values!!! yay

double gridWidth = e.NewSize.Width > e.NewSize.Height ? e.NewSize.Width : e.NewSize.Height;
double gridHeight = e.NewSize.Width > e.NewSize.Height ? e.NewSize.Height : e.NewSize.Width;

double w = Application.Current.Host.Content.ActualWidth;
double h = Application.Current.Host.Content.ActualHeight;
double hostWidth = w > h ? w : h;
double hostHeight = w > h ? h : w;

bool hasSoftButtons = hostWidth > gridWidth;

Just for your sanity.. I always want Landscape mode so width is always greater than height based on supported resolutions.. so there you have it.. subscribe to LayoutRoot’s SizeChanged and get the right size.. OnResize, the dimensions reflect 800 x 480 resolution and not 853 x 480.

Finally!

Images and Scaling with Windows Runtime #WinRT #WPDev #Windev

With the advent of Windows Phone 8.1, developers have the option of using WinRT native XAML stack as opposed to the Silverlight XAML the only option with early Windows Phone iterations.

One of the differences between WinRT XAML and Silverlight XAML is scaling and like with fonts, & images, scaling can have a significant effect on the look and feel of an app.

The default behavior with Windows Phone 8 was the inherent Silverlight behavior of scaling the image to fit. This meant that unless a high-resolution image was used, the image would end up looking blurred. Windows Runtime takes a different approach.

Windows 8x supports multitudes of screen sizes, resolutions and DPI. Windows Phone now supports 4 resolutions. The way Windows Runtime deals with this is by allowing developer to specify multiple images to match certain scale factors.

Windows Store Apps Windows Phone Store Apps
1.0 (100% no scaling) 1.0 (100% no scaling)
1.4 (140% scaling) 1.4 (140% scaling)
1.8 (180% scaling) 2.4 (240% scaling)

The runtime would determine the scaling factor to use depending upon the screen size, resolution, DPI and the form factor of device in question. The developer is required to supply images for scaling 1; the other scale factors are optional. If they are supplied, they will be used if necessary.

There are two ways of supplying images to support multi-scaling support.

  • Specify image scale within the file name e.g. Image.scale-100.png or Image.scale-140.png
  • Create direct for each image scale and place images inside without specifying scaling like in 1. E.g. scale-100/Image.png or scale-140/Image.png

The attached project includes examples of both ways.

To use this and enable auto-scaling support, you specify the image as specified below

<Image Source="Assets/Iris.png" />
<Image Source="Assets/Iris2.png" />

Occasionally it is necessary to set the images from code. In those instances, one should query the windows runtime to determine the current scaling factor and return appropriate image resource.

var uri = new System.Uri("ms-appx:///Assets/Iris.png");
var file = await StorageFile.GetFileFromApplicationUriAsync(uri);

This will correctly detect the scaling factor and get appropriate file.

Alternatively, for Windows 8x, we can use DisplayProperties.ResolutionScale to determine current scaling and for Windows Phone, we can use DisplayInformation.RawPixelsPerViewPixel.

Uri uri = null;
#if WINDOWS_PHONE_APP
    var rawpixelperview = Windows.Graphics.Display.DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel; 

    var Width = Convert.ToInt32(Window.Current.Bounds.Width * rawpixelperview); 
    var Height = Convert.ToInt32(Window.Current.Bounds.Height * rawpixelperview); 

    if(Width == 480 && Height == 800)
    {
        uri2 = new System.Uri("ms-appx:///Assets/scale-100/Iris2.png");
    }
    else if((Width == 720 && Height == 1280 ) || (Width == 768 && Height == 1280))
    {
        uri2 = new System.Uri("ms-appx:///Assets/scale-140/Iris2.png");
    }
    else
    {
        uri2 = new System.Uri("ms-appx:///Assets/scale-240/Iris2.png");
    }
#else
    switch (DisplayProperties.ResolutionScale)
    {
        case ResolutionScale.Scale100Percent:
            uri2 = new System.Uri("ms-appx:///Assets/scale-100/Iris2.png");
            break;

        case ResolutionScale.Scale140Percent:
            uri2 = new System.Uri("ms-appx:///Assets/scale-140/Iris2.png");
            break;

        case ResolutionScale.Scale180Percent:
            uri2 = new System.Uri("ms-appx:///Assets/scale-180/Iris2.png");
            break;
    }
#endif

var file = await StorageFile.GetFileFromApplicationUriAsync(uri);

It worth noting that images set in xaml and those done codebehind behave differently at least as far as size on screen is concerned. The only way to achieve correct size as visible in xaml is to set size to that of scale 1.0 image.

Supporting Accessibility

Windows Store apps support high contrast mode. The two available options are

  • Black background and white foreground
  • White background and black foreground.

To additionally support these two accessible image formats, you need to provide scale factor 1 images named / placed appropriately. I have excluded the high contrast images from sample as windows phone was using that by default.

Assets/Iris.scale-100_contrast-black.png

Assets/Iris.scale-100_contrast-white.png

or

Assets/scale-100_contrast-black/Iris2.png

Assets/scale-100_contrast-white/Iris2.png

Downloadable sample code can be downloaded here

WinRT License API, Silverlight 8.1 apps and sideload detection #wpdev

Since early 2011, I have been using the method described here.
Recently I have been upgrading all my apps to target Windows Phone 8.1 API. Most of previous API is still accessible in WP 8.1 SL8.1 apps. However a great deal more is available under WinRT API.

With my Alarm Clock app, I have started using WinRT API to pin and update Tiles. I have used WinRT API for background tasks as I can run them more frequently and I can detect Timezone change. I have added IAP to the app.
The app however is available in 2 flavours Free (with ads and restrictions) and Paid. So far I have only been updating the paid app. Of course I use sideload detection for Paid app however while considering update of Free version, I decided to use CurrenApp to execute License check and potentially offer to convert Free users in-place as opposed to getting them to buy another app!!

In good old SL API, I would have used

var license = new Microsoft.Phone.Marketplace.LicenseInformation();
IsTrial = license.IsTrial();

With WinRT that becomes

IsTrial = CurrentApp.LicenseInformation.IsTrial;

While developing and deploying if you observe the behaviour, the IsTrial never returns false. You cannot test Trial scenario without explicit pre-processor directives!
Of course CurrentAppSimulator is only available for WinRT app and not SL8.1 apps. The same behaviour would be exposed by the sideloaded apps. Not downloaded from store equates to full version. Like in good old days, WMAppPRHeader.xml is still attached after certification. So lets modify the code

try
{
   XDocument doc = XDocument.Load("WMAppPRHeader.xml");
 
   IsTrial = CurrentApp.LicenseInformation.IsTrial;
}
catch
{
   IsTrial = true;
}

You treat all apps without WMAppPRHeader.xml as trial. Job done!
Note that IAP are not affected. If you had TestIAP, it is not active by default.

CurrentApp.LicenseInformation.ProductLicenses[&amp;quot;TestIAP&amp;quot;].IsActive;

Happy coding!