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

Advertisement

Color Picker for #win8dev

I use colour picker across multiple projects on both windows phone and windows 8. Allowing users a choice of colour is a good thing from experience perspective. Whilst I found a number of solutions for Windows Phone, I never really found much for Windows 8.

Last year I ported the Coding4Fun ColorHexagonPicker to use with my TableClock app.Recenly working on Scribble I needed a broader range of colours and I decided to create an appbar control that conveniently lives in AppBar.

I have uploaded the source to Github. Feel free to fork and extent.

Screenshot (1)

 

WinRT and pointer manipulation #win8dev #winrtdev

We all know how Windows 8 introduced touch as a first class member in user input. If you have used Windows 8 it feels intuitive to touch.. hell I used to touch my non-touch laptop occasionally and not because I wanted to fondle it 🙂

Rewinding a bit.. in Silverlight and Windows Phone, we had a nice historic Touch.FrameReported event. This was the fastest way to detect pointer / touch interaction on the screen. It also supported multi-touch detection by means of exposing TouchFrameEventArges.GetTouchPoints.

We also know how famously .NET components were sliced and diced (lets leave it for some other time when I have had a drink or two). So its only natural that WinRT 1.0 native XAML didn’t have Touch.FrameReported.

The other high level mechanism for touch event detection was the set of manipulation events
* UIElement.ManipulationStarted
* UIElement.ManipulationDelta and
* UIElement.ManipulationCompleted

These made it to WinRT 1.0 XAML.. However there were changes. Now it comes armed with ManipulationMode.

The MEME RTFM definitely applies to me – I never read the manual… When we had proper MSDN lib for documentation it was all good.. now documentation is all over the place.. (Damn it… need to focus more… another thing to talk over a drink).

So what happened.. why trying to detect touch / pointer events, I started using Manipulation events but hell no.. the event’s just wouldn’t get called.. The mode has tons of options but to handle it yourself you need to set the ManipulationMode to All. Someone remind me something.. If I declare a bleeding event handler.. isn’t my intention already pretty clear ? (Something every logical from .NET lost in translation on its way to WinRT design).

So eventually I got that to work by setting ManipulationMode to All. Surprise surprise, the manipulation event args only expose a few things like Position and PointerType…So this definitely can’t do multi-touch.. sure it will detect all the touch inputs but it can’t tell the difference the two fingers!!

Time to give it the middle one and start looking at alternatives. Previously in WPF / Silverlight we had Mice events
* UIElement.MouseLeftButtonDown
* UIElement.MouseLeftButtonUp
* UIElement.MouseMove

Those didn’t directly made it. WinRT merged Mice and Touch events into Pointer events. So now we have
* UIElement.PointerPressed
* UIElement.PointerReleased
* UIElement.PointerMoved

The PointerRoutedEventArgs that are passed to the event handers not only give you current position and intermediate positions, they also give you access to Pointer associated with the event. Each touch point / mice button gets a unique id. This way you can detect activity of each touch points over the screen.

The post is very verbose and lot of gibberish.. let me throw some code snippet to lighten it up 🙂

private void LayoutRoot_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    Point position2 = e.GetCurrentPoint(this.LayoutRoot).Position;
    Pointer pointer2 = e.Pointer;
    FreeSketch(position2, pointer2);
}

private void FreeSketch(Point position2, Pointer pointer2)
{
    if (bDraw && pointer1.PointerId == pointer2.PointerId)
    {
        Line l = new Line()
        {
            X1 = position1.X,
            Y1 = position1.Y,
            X2 = position2.X,
            Y2 = position2.Y,
            StrokeThickness = 5,
            Stroke = new SolidColorBrush(Colors.Blue),
            Fill = new SolidColorBrush(Colors.Blue)
        };
        this.LayoutRoot.Children.Add(l); // adding to canvas
        this.position1 = position2;
    }
}

Happy coding

Search charm and user input gotchas #win8dev #winrtdev

A while back I implemented external and in-app search using search charm. This means that from anywhere in the app or even from main metro start screen, the user can start typing (which causes search to popup) and assuming they choose Cineworld, the search is passed off to my app.

Fast forward about 2 months back when I was implementing user submitted reviews for films and cinemas. I realised that the text input wouldn’t accept a single character – instead would keep showing the search charm….

aayyee.. that’s the search implementation… so I needed to selectively disable it depending upon where user is and what the user is up to.. trying to implement something meant going to layoutaware page so I don’t have to keep doing this.. whilst I was trying to figure out what to do next, I came across AllowSearch method.. strange.. then it occurred to me that I had added that while I was implementing search

 

protected void AllowSearch(bool bAllowed)
{
    SearchPane.GetForCurrentView().ShowOnKeyboardInput = bAllowed;
}

 
so I went about calling this.AllowSearch(false); in pages where I didn’t want search enabled.

Fast forward a bit closer, last week, I was using WebView allow ticket purchase in-app.. I tried clicks etc but not the full login etc as I know that already works and that’s implemented by Cineworld themselves.. I submit that app and a user contacts me saying.. they can’t book tickets.. search charm keeps popping up…

Damn it.. I had totally forgotten about AllowSearh method. Lets hope this serves as a reminder to myself to remember this in future.
Happy coding

WebView and working with C# and XAML #win8dev

Many of my Cineworld app users whether its Windows Phone or Windows 8 complain about lack of in-app ticketing. In fact I would personally as a user prefer otherwise – Use tried and trusted website for ticketing as opposed to an app – sure my app might be good enough to be an official app but that’s a different issue.

So what did I do, well for Windows Phone 7 and 8, I used WebBrowser control and added in-app use of Cineworld mobile website. Most users are happy – can’t please everyone. I do remember that WebBrowser control offered many more events for management in Windows Phone 8 than in Windows Phone 7. So how about Windows 8 ?

Well Windows 8 / WinRT 1.0 ships with WebView control. This is similar is many ways to Windows Phone 7 WebBrowser control. Why do I say that ? Well, it contains LoadCompleted event to inform about page loaded but no loading event. Doesn’t expose the internal navigation stack and is substantially more painful than Windows Phone 7 Browser Control 😐

I am also aware that Windows 8.1 / WinRT 1.1 (??) ships with much improved WebView that exposes all the relevant event without hacks

Navigating Event: A quick search on the net found me Limitations of the WebView in Windows 8 Metro Apps. The post along side How to emulate Navigating event in the WebView turned out to be very useful. I used the WebViewWrapper to add Navigating event.

LoadCompleted Event: This event was exposed by the WebView and I used both maintain NavigationStack and toggle progress ring visibility

Progress Ring: Nothing can sit on top of WebView – no matter what order you define the elements in XAML. One needs to set WebView to collapsed visibility to show any other xaml content. I tend to toggle opacity to 0.5 and then show progress ring however this was a kill-joy till I found this “How to put a ProgressRing over a WebView” – easy peasy.. use WebViewBrush and paint a rectangle and hide the webview.. user cant really tell 🙂

Navigation Stack: Most pages in a Windows 8 apps expose back button. By default the back button calls GoBack defined in LayoutAwarePage.cs. It is however overrideable. So what should be behaviour be ? Well if you have been browsing a few pages, backbutton should ideally unwind that stack. Since WebView doesn’t really expose Nav Stack (just like Windows Phone 7 API), we need to do the dirty work. Use Completed to push the Uri. On Back button press pop the Uri. Apart from that, we just use InvokeScript to use browser’s internal nav stack using javascript.

<Grid Background="#FF231F20">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <WebView Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" x:Name="wbCineworld" />
    <Rectangle Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Name="MaskRectangle"/>

    <Grid x:Name="gProgress" Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="100" Background="White" Visibility="Collapsed">
        <ProgressRing x:Name="prProgress" HorizontalAlignment="Center" VerticalAlignment="Center" IsActive="True" Height="80" Width="80" Foreground="#FFB51C10"/>
    </Grid>

    <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
    <Button Grid.Column="0" Grid.Row="1" VerticalAlignment="Bottom" x:Name="btnExitTicketing" Style="{StaticResource DeleteAppBarButtonStyle}" AutomationProperties.Name="Exit ticketing" Click="btnExitTicketing_Click" />
</Grid>
public sealed partial class InAppBrowser : Cineworld.Common.LayoutAwarePage
{
    public InAppBrowser()
    {
        this.InitializeComponent();

        var wrapper = new WebViewWrapper(this.wbCineworld);
        wrapper.Navigating += WrapperNavigating;
    }

    void WrapperNavigating(object sender, NavigatingEventArgs e)
    {
        this.SpinAndWait(true);
    }

    private Stack<Uri> NavigationStack = new Stack<Uri>();
    public static Uri NavigationUri { get; set; }

    private void SpinAndWait(bool bNewVal)
    {
        WebViewBrush brush = new WebViewBrush();
        brush.SourceName = "wbCineworld";
        brush.Redraw();
        MaskRectangle.Fill = brush;

        this.wbCineworld.Visibility = bNewVal ? Windows.UI.Xaml.Visibility.Collapsed : Windows.UI.Xaml.Visibility.Visible;
        this.MaskRectangle.Opacity = bNewVal ? 0.5 : 1;

        this.MaskRectangle.Visibility = bNewVal ? Windows.UI.Xaml.Visibility.Visible : Windows.UI.Xaml.Visibility.Collapsed;

        this.gProgress.Visibility = bNewVal ? Windows.UI.Xaml.Visibility.Visible : Windows.UI.Xaml.Visibility.Collapsed;
        this.prProgress.IsActive = bNewVal;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        this.wbCineworld.LoadCompleted += wbCineworld_LoadCompleted;

        this.wbCineworld.Navigate(NavigationUri);

        this.SpinAndWait(true);
    }

    void wbCineworld_LoadCompleted(object sender, NavigationEventArgs e)
    {
        this.SpinAndWait(false);
        NavigationStack.Push(e.Uri);
    }

    protected override void GoBack(object sender, RoutedEventArgs e)
    {
        if (this.NavigationStack.Count > 1)
        {
            this.wbCineworld.InvokeScript("eval", new string[1] { "history.go(-1)" });

            // note that this is another Pop - as when the navigate occurs a Push() will happen
            NavigationStack.Pop();
        }
        else
            base.GoBack(sender, e);
    }

    private void btnExitTicketing_Click(object sender, RoutedEventArgs e)
    {
        base.GoBack(sender, e);
    }
}

Let me just reiterate – I haven’t found something new, I have just found a few bits and have put them together. Happy coding.

Note: Meant to post this earlier.. I am checking NavigationStack.Count and ideally the check should be > 0 but in my case, there was an additional redirection and hence I am using Count > 1.

Persistent Thread like behavior with #win8dev

As you might have gathered, I
* like writing bad code
* doing things that people frown upon (like writing bad code)

Ever since I started Windows 8 Development, I miss Threads in all their glory. Sure Async Task is mostly fine but its not the same. Earlier today, Arafat asked a question about truly async logging mechanism. I suggested hacking Async Task into a thread like behaviour 🙂 – actually it just gets you a threadpool thread 🙂

Task.Run provides a mechanism to queue given code on a ThreadPool for parallel execution. What happens when you refuse to return the method ? well you get the ThreadPool thread as your personal bitch 🙂

CancellationTokenSource CancellationSource = null;
CancellationToken TaskCancellationToken;

private async void AlwaysOnLoggingAsyncTask()
{
     while (!this.TaskCancellationToken.IsCancellationRequested)
     {
          // do something.. actually do anything
          // in the sample I was mucking around with StorageFile
          // hence the async void 

          await Task.Delay(1000); // don't kill the PC by running a tight loop.
     }
}

private async void btnStart_Click(object sender, RoutedEventArgs e)
{
     CancellationSource = new CancellationTokenSource();
     this.TaskCancellationToken = CancellationSource.Token;
     Task.Run(() => this.AlwaysOnLoggingAsyncTask(), TaskCancellationToken);
}

private void btnStop_Click(object sender, RoutedEventArgs e)
{
     CancellationSource.Cancel();
}

Here’s a dummy sample logging project that incorporates this bad code 🙂

Update: since I was writing really bad code, I deliberately forgot to add Task.Delay. If you prefer to not write code that makes system a bit less miserable, consider adding a Task.Delay of about 1000 (1 second)

Update 2: Jarno pointed out that I am writing really really bad code – mechanism dating pre-Task era – which is correct. Old habits die hard. Well I’ve update the blog plus source 😛 to use Task Cancellation Token

Semantic zoom – beyond the stock behaviour – #windev #winrt

Update: Download a sample implementation from skydrive

Last year I was working on Cineworld app for Windows 8. Having use LongListSelector I wanted to ensure that I can transfer the look and feel. However I was fighting multiple battles – the first one is that while LongListSelector is a single control, SemanticZoom control itself contains ZoomInView and ZoomedOutView. These are independent of each other and most examples then to show settings data to zoomed in view and then zoomed out view referring to zoomed in view. It gets messy – maybe I am just slow but it was not intuitive and took me ages.

While mucking around, I came across this post and from there, kept trying till I got what I wanted.

LLS1LLS 

Screenshot (220)

Screenshot (221)

 

<Page.Resources>
    <CollectionViewSource x:Name="cvsFilms" IsSourceGrouped="true" />

    <!-- Zoomed out templates and selectors -->
    <DataTemplate x:Key="GroupTemplate">
        <Border Margin="6" Width="75" Height="75" Background="Black" >
            <TextBlock Text="{Binding Name}" Foreground="White" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Border>
    </DataTemplate>
    <DataTemplate x:Key="EmptyGroupTemplate">
        <Border Margin="6" Width="75" Height="75" BorderBrush="Black" BorderThickness="1" Background="LightGray">
            <TextBlock Text="{Binding Name}" Foreground="Black" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Border>
    </DataTemplate>
    <local:EmptyOrFullSelector
            x:Key="FilmGroupEmptyOrFullSelector"
            Empty="{StaticResource EmptyGroupTemplate}"
            Full="{StaticResource GroupTemplate}" />
    <DataTemplate x:Key="GroupHeaderTemplate">
        <Border Margin="20, 20, 0, 0" Width="75" Height="75" Background="Black" Tapped="Grid_Tapped">
            <TextBlock Text="{Binding Key}" Foreground="White" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Border>
    </DataTemplate>
    <DataTemplate x:Key="GroupItemTemplate">
        <Border Margin="14, 20, 0, 0">
            <Grid Height="350" Width="185" Margin="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Image  Source="{Binding PosterUrl}" Height="278" Width="185" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
                <TextBlock Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Title}" HorizontalAlignment="Center" FontFamily="Segoe UI" />
            </Grid>
        </Border>
    </DataTemplate>
</Page.Resources>

<SemanticZoom x:Name="semanticZoom" Background="White" HorizontalAlignment="Stretch" Grid.Row="1" VerticalAlignment="Stretch">
    <SemanticZoom.ZoomedOutView>
        <GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False" HorizontalAlignment="Center" ItemTemplateSelector="{StaticResource FilmGroupEmptyOrFullSelector}" >
            <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapGrid Background="LightGray" MaximumRowsOrColumns="9" VerticalChildrenAlignment="Center" Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
        </GridView>
    </SemanticZoom.ZoomedOutView>
    <SemanticZoom.ZoomedInView>
        <GridView x:Name="gvZoomedInFilms" ItemsSource="{Binding Source={StaticResource cvsFilms}}" IsSwipeEnabled="True" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ItemTemplate="{StaticResource GroupItemTemplate}">
            <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
            <GridView.GroupStyle>
                <GroupStyle HeaderTemplate="{StaticResource GroupHeaderTemplate}">
                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,50,0"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </GridView.GroupStyle>
        </GridView>
    </SemanticZoom.ZoomedInView>
</SemanticZoom>
List<GroupInfoList<object>> dataLetter = null;

public ListFilms()
{
    this.InitializeComponent();

    FilmData cd = new FilmData(App.Films);

    dataLetter = cd.GroupsByLetter; 
    cvsFilms.Source = dataLetter;
    gvZoomedInFilms.SelectionChanged -= gvZoomedIn_SelectionChanged;
    gvZoomedInFilms.SelectedItem = null;
    (semanticZoom.ZoomedOutView as ListViewBase).ItemsSource = cd.FilmHeaders;     gvZoomedInFilms.SelectionChanged += gvZoomedIn_SelectionChanged;

    semanticZoom.ViewChangeStarted -= semanticZoom_ViewChangeStarted;
    semanticZoom.ViewChangeStarted += semanticZoom_ViewChangeStarted;
}

void semanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{
    if (e.SourceItem == null)
        return;

    if (e.SourceItem.Item.GetType() == typeof(HeaderItem))
    {
        HeaderItem hi = (HeaderItem)e.SourceItem.Item;

        var group = dataLetter.Find(d => ((char)d.Key) == hi.Name);
        if (group != null)
            e.DestinationItem = new SemanticZoomLocation() { Item = group };
    }
}

// FilmData looks like this
private List<GroupInfoList<object>> groupsByLetter = null;

public List<GroupInfoList<object>> GroupsByLetter
{
    get
    {
        if (groupsByLetter == null)
        {
            groupsByLetter = new List<GroupInfoList<object>>();

            var query = from item in Collection
                        orderby ((FilmInfo)item).Title
                        group item by ((FilmInfo)item).HeaderChar into g
                        select new { GroupName = g.Key, Items = g };

            foreach (var g in query)
            {
                GroupInfoList<object> info = new GroupInfoList<object>();
                info.Key = g.GroupName;
                foreach (var item in g.Items)
                {
                    info.Add(item);
                }
                groupsByLetter.Add(info);
            }
        }

        return groupsByLetter;
    }
}

List<HeaderItem> filmHeaders = null;
public List<HeaderItem> FilmHeaders
{
    get
    {
        if (filmHeaders == null)
        {
            filmHeaders = new List<HeaderItem>();

            char c = '#';

            filmHeaders.Add(new HeaderItem() { Name = '#', IsEnabled = this.GroupsByLetter.Exists(k => ((char)k.Key) == c) });
                    
            for (int i = 65; i <= 90; i++)
            {
                c = (char)i;

                if (this.GroupsByLetter.Exists(k => ((char)k.Key) == c))
                    filmHeaders.Add(new HeaderItem() { Name = c, IsEnabled = true });
                else
                    filmHeaders.Add(new HeaderItem() { Name = c, IsEnabled = false });
            }                    
         }

        return filmHeaders;
    }
}

I think that’s about it. Its been 6 months now and I think I am going to change how it looks 🙂 some stuff will remain as it is, others will have to change

Background Audio #win8dev #winrt

In this post I am going to detail some of the work I have been doing on Background Audio. But before I get onto it, let me say that the last few months have been crazy / busy, not highly motivational for personal dev etc. I have done a few updates over the last few months but no new development until this month.

About 2 weeks back I started work on Porting Slydr code to WPF control and that was a breeze. Infact last night I saw something about a simple app being tested.. makes me think on whether I should beat them to it 🙂 or not.

Now back to Background Audio. With Windows Phone, one had to create a Background Audio Player which the app and system would then communicate with. The app could kick the Background Audio Player off and then exit and the audio would keep on playing. I have talked about this before and will saying it again. WPDev 7x provided many ways of playing audio. With Windows 8 and #WinRT your options are restricted. I have gotten used to giving MediaElement more usage and credit for what it does.

<MediaElement x:Name="mePlayer" />

All you need to do is set the Source property and then call Play(). The audio starts playing. If the app gets suspended, the music stops playing. To enable background audio there are a number of things you need to do.
* Firstly set MediaElement‘s AudioCategory to BackgroundCapableMedia. This still doesn’t activate the Background Audio as its only the first step

* Now open package.appmanifest file. Open the Declarations tab and add Background Tasks. While you are there set the Supported task types property to Audio and Control Channel. You will also need to set the Start page in App settings. Of course now that you have done this, you will have to create a Badge logo before you can save the changes to appmanifest.
appManifest

* Now back to codebehind of your page and add event handlers for MediaControl‘s PlayPressed, PausePressed, StopPressed and PlayPauseTogglePressed events

In my app I also support Previous Track / Next Track so I additionally added support for PreviousTrackPressed and NextTrackPressed events.

MediaControl.PlayPressed += MediaControl_PlayPressed;
MediaControl.PausePressed += MediaControl_StopPressed;
MediaControl.StopPressed += MediaControl_StopPressed;
MediaControl.PlayPauseTogglePressed += MediaControl_PlayPauseTogglePressed;
MediaControl.NextTrackPressed += MediaControl_NextTrackPressed;
MediaControl.PreviousTrackPressed += MediaControl_PreviousTrackPressed;

and

void MediaControl_PreviousTrackPressed(object sender, object e)
{
    this.Previous();
}

void MediaControl_NextTrackPressed(object sender, object e)
{
    this.Next();
}

void MediaControl_PlayPauseTogglePressed(object sender, object e)
{
    if (MediaControl.IsPlaying)
        this.Stop();
    else
        this.Play();
}

void MediaControl_StopPressed(object sender, object e)
{
    this.Stop();
}

void MediaControl_PlayPressed(object sender, object e)
{
    this.Play();
}

Now when you run the app, initiate the audio playback and navigate away from the app.. Bingo. the audio is still playing.

Quirk: App invocation from secondary tile with app currently running #win8dev

My cineworld app for Windows 8 is now in Store and thanks to many of my dev friends, I am getting bug reports and feature requests.

Anthony Wieser just found another bug for me. The app is running and you have two cinemas in pinned state: Enfield and Cambridge. Hit Start and open one of the pinned cinema, the app shows the view you left it at. No navigation to desired cinema.

To resolve this issue, I set the app to debug and then opened App.xaml.cs and as I was putting a break point in OnActivatedMethod, I saw this

// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
   Window.Current.Activate();
   return;
}

var rootFrame = new Frame();
SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

if (rootFrame.Content == null)
{
... // so on and so forth
}

this is the culprit. What I should be doing is checking params passed and then showing the correct xaml page. time to fix it now.

Update: Fixed. here’s what I have done 🙂

if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
    if (iCin == int.MinValue)
    {
        Window.Current.Activate();
        return;
    }
    else
        rootFrame = (Frame)Window.Current.Content;
}
else
{
    // Create a Frame to act as the navigation context and associate it with
    // a SuspensionManager key
    rootFrame = new Frame();
    SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
}

if (rootFrame.Content == null || iCin != int.MinValue) // the iCin contain value passed in activation param
{
... // so on and so forth
}

once you do this, you can handle navigation params and navigate to correct xaml page.

Implement Search Contract the easy way #win8dev

Most apps contain some sort of data that you as a developer would like exposed. That’s were the Search Contract comes into play. Say the user is on the start screen and wishes to search your app, they could just start typing and select your app from the list. If you implemented Search contract, your app will be listed in the app list and the user could just type away and hit enter to see results. That would be nice wouldn’t it ?

Lets get on with it and add search contract in a simple / easy way.
Right click on the Win8 project and select Search Contract. Name it whatever you wish and hit enter.

This does two things:

  • Adds override for OnSearchActivated in your App.xaml.cs
  • Adds required page for search and display of results.

This is what the OnSearchActivated looks like

protected async override void OnSearchActivated(Windows.ApplicationModel.Activation.SearchActivatedEventArgs args)
{
    // TODO: Register the Windows.ApplicationModel.Search.SearchPane.GetForCurrentView().QuerySubmitted

    // event in OnWindowCreated to speed up searches once the application is already running

    // If the Window isn't already using Frame navigation, insert our own Frame
    var previousContent = Window.Current.Content;
    var frame = previousContent as Frame;

    // If the app does not contain a top-level frame, it is possible that this 
    // is the initial launch of the app. Typically this method and OnLaunched 
    // in App.xaml.cs can call a common method.
    if (frame == null)
    {
        // Create a Frame to act as the navigation context and associate it with
        // a SuspensionManager key
        frame = new Frame();
        Cineworld.Common.SuspensionManager.RegisterFrame(frame, "AppFrame");

        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            // Restore the saved session state only when appropriate
            try
            {
                await Cineworld.Common.SuspensionManager.RestoreAsync();
            }
            catch //(Cineworld.Common.SuspensionManagerException)
            {
                //Something went wrong restoring state.
                //Assume there is no state and continue
            }
        }
    }

    frame.Navigate(typeof(SearchResults), args.QueryText);
    Window.Current.Content = frame;

    // Ensure the current window is active
    Window.Current.Activate();
}

as you can see it is passing the search query to the Search Results page while navigating.
Now lets look at the code-behind in SearchResults.xaml.
FYI, I am loading my dataset if required in the OnNavigatedTo method. This page is a simple results display page. It contains GridView / ListView to display results. Lets look at the LoadState method

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    var queryText = navigationParameter as String;

    List<SearchResult> matches = new List<SearchResult>();

    foreach (var f in App.Films.Values)
    {
        IEnumerable<CastInfo> casts = from cast in f.FilmCast
                                        where cast.Name.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)
                                        select cast;

        foreach (var c in casts)
        {
            //searchCinemas.Add(c.Name, c);
            matches.Add(new SearchResult() { Name = c.Name, Subtitle = String.Format("{0} in {1}", c.Character, f.Title), SearchObject = f, Image = c.ProfilePath });
        }
    }

    IEnumerable<FilmInfo> films = from film in App.Films.Values
                                    where film.Title.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)
                                    select film;

    searchFilms.Clear();
    searchCinemas.Clear();

    foreach (var f in films)
    {
        searchFilms.Add(f.Title, f);
        matches.Add(new SearchResult() { Name = f.Title, Image = f.PosterUrl, SearchObject = f });
    }

    IEnumerable<CinemaInfo> cinemas = from cinema in App.Cinemas.Values
                                        where cinema.Name.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)
                                        select cinema;

    foreach (var c in cinemas)
    {
        searchCinemas.Add(c.Name, c);
        matches.Add(new SearchResult() { Name = c.Name, SearchObject = c, Image = new Uri("ms-appx:///Assets/Background.png") });
    }
            
    // TODO: Application-specific searching logic.  The search process is responsible for
    //       creating a list of user-selectable result categories:
    //
    //       filterList.Add(new Filter("<filter name>", <result count>));
    //
    //       Only the first filter, typically "All", should pass true as a third argument in
    //       order to start in an active state.  Results for the active filter are provided
    //       in Filter_SelectionChanged below.

    // Communicate results through the view model
    this.DefaultViewModel["QueryText"] = '\u201c' + queryText + '\u201d';

    bAllowNav = false;

    this.DefaultViewModel["Results"] = matches;

    bAllowNav = true;

    var filterList = new List<Filter>();
    filterList.Add(new Filter("All", 0, true));

    //this.DefaultViewModel["Filters"] = filterList;
    //this.DefaultViewModel["ShowFilters"] = filterList.Count > 1;
}

I added code to search my dataset and to add results to DefaultViewModel. I don’t use MVVM but the default code implemented basic binding etc and I thought I’d leave it at that. I accumulate the results and save them and let bindings do the rest. Let me show you how it looks
Say I got to start screen, type I ben and select my app, this is what I get.

Now you have a nice list of matched entries. Now suppose I wanted to let users click on a matched entry and to navigate them to details page ? I implemented GridView ItemClick event (SelectionChanged doesn’t work for some reason)

private void resultsGridView_ItemClick(object sender, ItemClickEventArgs e)
{
    if (e.ClickedItem != null && bAllowNav)
    {
        SearchResult res = (SearchResult)e.ClickedItem;
        if (res.SearchObject is FilmInfo)
        {
            FilmDetails.SelectedFilm = (res.SearchObject as FilmInfo);
            this.Frame.Navigate(typeof(FilmDetails));
        }
        else if (res.SearchObject is CinemaInfo)
        {
            CinemaDetails.SelectedCinema = (res.SearchObject as CinemaInfo);
            this.Frame.Navigate(typeof(CinemaDetails));
        }
    }
}

Hope this post is helpful to others looking to implement the search contract. Happy coding!