Customising Panorama control #wpdev #wp7dev #wp8dev

This control is deemed like the showcase control in windows phone. Its simple, its elegant and non of the other platforms have anything similar. Its been described as being synonymous to a magazine cover. Dave Crawford’s design talk is a great intro to this

Most Modern UI stock templates use a very typographical layout. Its all textual with no branding samples in sight – unlike real life where branding is a foremost part of the experience. Here is link to dev center’s article http://msdn.microsoft.com/library/windowsphone/design/hh202892(v=vs.105).aspx This article talks about various aspects of using panorama control etc.

For myself, the first time I was tempted into using Panorama was after playing with the new Facebook Beta app. This app divided windows phone community by marking a significant departure from textual nature of panorama. So far in Cineworld app, I had restricted myself to Pivot but I could see myself using panorama..

Branding:If an app requires specific branding, its best to create a user control that provides the option to set Brand logo and optional text messages. One isn’t restricted to a two line header on each page :)

wp_ss_20130619_0003  wp_ss_20130619_0004

As you can see, instead of excessively large page header, the customised header is very compact and since its a user control, we can consume it across other pages within the app. So what can we do with Panorama, well it exposes a TitleTemplate. We take the UC and we shove that in TitleTemplate. You can further customise the HeaderTemplate to change how panel headers look like.

Film

I am definitely not saying you should do what I did. However what I will state is that Metro or Modern Design is really just about good design. Just because stock templates do it one way doesn’t mean no one should do otherwise. Kudos the designers of the new Facebook BETA app for doing something different.

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

FlipTile, CycleTile and various iterations of #wpdev sdks

As you might have realised, the last few weeks, cineworld app has been my obsession. I started work on WP8 port and then back-ported some changes to WP7 version of the app (its the only one that’s live at the moment so makes sense). So what did I do so far on it (WP7 version) ?

1) Moved to using 50% space when displaying film posters, moved film little on top of the posters, removed the button style that caused me many xaml exceptions on WP8
2) Moved to using good quality images only for ImageTile control.
3) Added background task to download data files and to set Tile images

This is live as we speak and a few people emailed me saying it looks great. Now I am not going to create yet another background task for WP8 version (since I wanted to use Cycle Tile Template for WP8 version of the app.
Microsoft’s own tile related pages show how to support newer tiles for WP7.8 and WP8 even though the app itself is built on WP 7.1 (Mango) sdk

So far I stayed away from temptation of managing image repository on the device. you can see that it gets messy eventually and you have to clean up and what not.. however after implementing basic solution, it started throwing exceptions.. files used for tiles cannot be remote. Bugger.. more work

Step 1): Download image files locally. I have been using AsyncWebClient (Custom wrapper with TaskCompletionSource) exposing awaitable DownloadFile method. Plug that in and download all the files. Its background tasks, I am not really fussed about parallel execution.. just manageable execution (read sync like)

Step 2): For Uri array using relative path and ??? still nothing. More work, a quick search about the net found me a couple of posts for “live tile isolated storage image WP7″ File Path in Isolated File Storage is the one that I first opened and a quick scan reminded me of “isostore” based Uri

Step 3): Save all the images to Shell\SharedContent and switch all Uri to use isotore:/Shell/SharedContent/Filename.jpg

The code below will help you create live tiles with new tile templates from WP7.1 (Mango) sdk based project. I will be using the same code for WP8 in coming days since its the same background task

A word to the wise:
* You cannot determine if the template used by Tile is Standard or a new one.
* What I now do is Create a CycleTileData template when user wants to pin (if WP7.8 or WP8) else StandardTileData
* When iterating tiles, I try CycleTileData template and failing that try StandardTileData for update

Code snippets that might help:

Add AppExtra above App element in WMAppManifest.xml file (works for both WP7.8 and WP8)

<AppExtra xmlns="" AppPlatformVersion="8.0">
  <Extra Name="Tiles"/>
</AppExtra>
public async Task DownloadFileAsync(string url, string filename, string folder = null)
{
    // create a web client for downloading the string
    var wc = new WebClient();

    try
    {
        IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();

        string file = (folder == null ? filename : String.Format("{0}/{1}", folder, filename));
                
        using (Stream s = await wc.OpenReadTaskAsync(url))
        {
            using (Stream slocal = isf.OpenFile(file, FileMode.Create))
            {
                s.CopyTo(slocal);
            }
        }
    }
    catch (Exception ex)
    {
        if (1 == 1) // just to check exceptions during debugging. do whatever pleases you
        {
        }
    }
}

//works for both WP7.8 and WP8
private static Version TargetedVersion = new Version(7, 10, 8858);
public static bool IsTargetedVersion { get { return Environment.OSVersion.Version >= TargetedVersion; } }

private static void SetTileBackground(string[] filesToShow, string folder, Random random, ShellTile currentTile)
{
    bool tryFlip = false;

    if (Config.IsTargetedVersion)
    {
        try
        {
            Uri smallimage = new Uri("Images/CycleSmall.png", UriKind.Relative);
            Uri mediumimage = new Uri("Images/CycleMedium.png", UriKind.Relative);

            UpdateCycleTile(smallimage, mediumimage, folder, filesToShow, random, currentTile);
        }
        catch
        {
            tryFlip = true;
        }
    }
    else
        tryFlip = true;
            
    if(tryFlip)
    {
        int rand = random.Next(0, filesToShow.Length);

        StandardTileData NewTileData = new StandardTileData
        {
            BackBackgroundImage = new Uri(String.Format("isostore:{0}/{1}", folder, filesToShow[rand]), UriKind.Absolute)
        };

        currentTile.Update(NewTileData);
    }
}

public static void UpdateCycleTile(
    Uri smallBackgroundImage, Uri backgroundImage,
    string folder, string[] filesToShow, Random random, ShellTile currentTile)
{
    // Get the new cycleTileData type.
    Type cycleTileDataType = Type.GetType("Microsoft.Phone.Shell.CycleTileData, Microsoft.Phone");

    // Get the ShellTile type so we can call the new version of "Update" that takes the new Tile templates.
    Type shellTileType = Type.GetType("Microsoft.Phone.Shell.ShellTile, Microsoft.Phone");

    // Get the constructor for the new FlipTileData class and assign it to our variable to hold the Tile properties.
    var UpdateTileData = cycleTileDataType.GetConstructor(new Type[] { }).Invoke(null);

    // Set the properties. 
    SetProperty(UpdateTileData, "SmallBackgroundImage", smallBackgroundImage);

    Uri[] mediumImages = new Uri[9];

    mediumImages[0] = backgroundImage;
    for (int i = 1; i < 9; i++)
    {
        int rand = random.Next(filesToShow.Length);

        mediumImages[i] = new Uri(String.Format("isostore:{0}/{1}", folder, filesToShow[rand]), UriKind.Absolute);
    }

    SetProperty(UpdateTileData, "CycleImages", mediumImages);

    SetProperty(UpdateTileData, "Title", "my cineworld");
            
    // Invoke the new version of ShellTile.Update.
    shellTileType.GetMethod("Update").Invoke(currentTile, new Object[] { UpdateTileData });
}

private static void SetProperty(object instance, string name, object value)
{
    var setMethod = instance.GetType().GetProperty(name).GetSetMethod();
    setMethod.Invoke(instance, new object[] { value });
}

This by no means is fine / perfect code. Please treat it as a PoC and do whatever you do normally

Geolocator and MovementThreshold #wpdev #win8dev

Last few months, I have had too much on my plate. Been doing lot to Windows 8 and Phone development. Surprisingly Microsoft UK is keeping tabs on phone developers who aren’t adapting to #WP8 and seemingly, I was invited to a dev event targeting the likes of me :P

Sure I am going and in anticipation of the event, I decided to port Cineworld app to WP8. Its been a challenging task. Reworking a few bits of LongListSelector, Visual Studio / Blend disliking all the xaml and of course a button style causing xaml exception. If that wasn’t enough:

Having used Geolocator in Windows 8 app, I decided to use the same for WP8 as its usually a lot faster than GeoCoordinateWatcher. I moved the code to be more like Win8 and noticed that the location detection was flaky at best. Now I did the stupid thing and like in WP7 version, I decided to set both DesiredAccuracy to High and MovementThreshold to 0 thinking absolutely nothing.

Of course that didn’t help it, it made matters worse, instead of flaky behaviour, it decided to call it quits.

var pos = await locator.GetGeopositionAsync(TimeSpan.FromMinutes(5), TimeSpan.FromSeconds(2));

instead of timing out, it started to hang. In desperation, I started looking at other events exposed by this class and tried hooking into StatusChanged event. Now things were bad, I noticed that it was getting operation aborted exception and decided that something was seriously wrong and that I should take a nap instead :)

Of course the problem was still there when I woke up the next day :P its only later that I decided to search on Geolocation and operation aborted and realised that I wasn’t the only one and it was all my fault – causality of me setting the MovementThreshold to 0. I set it to 10 instead and bingo.. it all works.

While I didn’t find it, I hope this post helps people like an earlier post helped me.

What’s wrong with Windows 8

Everyone’s doing it and why should I not do the same ? Yeah yeah everyone’s been bad mouthing how horrible windows 8 and thought, why should I be left behind. So here’s my 2p on the subject.

I remember being one of the first to get a laptop with XP.. having used Windows 95 / 98 and ME plus Windows NT, Windows 2000, I was looking forward to it. I was young, naïve and stupid (actually last two don’t have an expiration date). I loved it yet in tons of places I kept reading that the changes were too far and users need to educate themselves yet again.

Just read this one.. it’s not that bad.. if you really want me to dig some, I will try

http://reviews.cnet.com/windows/microsoft-windows-xp-home/4505-3672_7-6534881.html

Of course we know how that went, XP did eventually coerce people into converting.. XP however wasn’t all that hyped..

Next up, we had the long anticipated Longhorn.. I was still relatively young (naïve and stupid) and what did I do, I installed the leaked alpha on my PC and played with it. Was a little disappointed not to see .NET integration into the system but oh well it was only an alpha.. things dragged on and we had Vista.

Vista was great, it did everything XP did and even more. There was only one drawback and that was a new shiny driver model. Crap Microsoft gave the OEMs just over a year to prepare drivers… Of course what happened, Vista was slated because of UAC and being buggy as hell (thank you OEMs.. my personal favourite was NVidia.. their drivers were worse than stock shipped by Vista).

“you can put lipstick on a pig, but it is still a pig

All Microsoft did was to release rebranded Vista as Windows 7. Sure there were a few changes like toning down the UAC tiny bit but the main thing was that drivers had already matured in this time and bingo.. people loved Windows 7 like they loved XP..

Of course around that time, iPhones came and iPads came and Droids started running riot.. Microsoft was under pressure to deliver and they were working on Windows 8. The Metro UX plus a tiled interface was their answer. And they took away the start button. Just look at the two screen and tell me what you think of those !!

Messy-Desktop

Image

Most PCs after a few months of usage and installation crap-ware look like Picture 1. Is Windows 8 Metro UI that bad ? It looks cleaner and simpler, the icons are larger. Users can choose to pin / unpin what Start Screen shows. I keep hearing that power users hate Metro UI. What exactly is a power user ? My personal take is someone who has become adapt at clicking the icons on the desktop like in Picture 1. By that definition, I was never really a power user. Neither is my wife nor is my daughter, in fact they don’t even care about desktop forget the start menu

So if this is not a criticism of the larger easily clickable icons, what is it about ? You know it.. it’s the little blue orb that was called the Start Button. That thing that has been usually in the bottom left corner. This is about “Who moved my cheese?”
All would have been fine, if Microsoft had just left that in place and used that to toggle visibility of Metro UI / Start Screen.

Oh Microsoft what have you done !!

MVVM and me – Part 4

Its taken me ages to get things done. I am now at the last xaml screen and I am so tired. Last week I spent a bit of time going through AppBar.

I keep whining that Microsoft built this great Binding and Command mechanism in XAML however it was only a half hearted attempt and that they don’t really care whether you use MVVM or not.

AppBar is the perfect example of that. It does not support either.  So the MVVM and XAML community (actually a few developers) have taken the task to themselves. They have created custom AppBar controls that support  Binding and Commanding infrastructure.

So what controls did I find ? there’s

  1. http://appbarutils.codeplex.com/
  2. https://github.com/PedroLamas/Cimbalino-Phone-Toolkit
  3. http://bindableapplicationb.codeplex.com/

 
So what is the experience using them. The first two require you to set the appbar within Behaviours. The 3rd one is the closest to the native AppBar in terms of XAML and simplicity.

The stock WP7 xaml AppBar needs to be set list this.

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar>
        <shell:ApplicationBarIconButton x:Name="btnLast" IconUri="/Images/appbar.image.png" Text="test" Click="btbTest_Click"></shell:ApplicationBarIconButton>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

AppBarUtils and Cimbalino require something like this

<i:Interaction.Behaviors>
    <cimbalinoBehaviors:MultiApplicationBarBehavior SelectedIndex="{Binding ApplicationBarSelectedIndex}">
        <cimbalinoBehaviors:ApplicationBar>
            <cimbalinoBehaviors:ApplicationBarIconButton Command="{Binding AddItemCommand, Mode=OneTime}" IconUri="/Images/appbar.add.rest.png" Text="add" />
        </cimbalinoBehaviors:ApplicationBar>
    </cimbalinoBehaviors:MultiApplicationBarBehavior>
</i:Interaction.Behaviors>

The Bindable AppBar

<bar:Bindable.ApplicationBar>
    <bar:BindableApplicationBar IsVisible="{Binding BarIsVisible}"
        <bar:BindableApplicationBarButton Text="{Binding IconButtonText}" IconUri="{Binding IconUri, FallbackValue=/Icons/Dark/appbar.add.rest.png}" IsEnabled="{Binding ButtonIsEnabled}" />
    </bar:BindableApplicationBar>
</bar:Bindable.ApplicationBar>

The biggest downside is that they do not render at design time. The single biggest advantage of MVVM goes out of the window. You need to use standard appbar in design more and them once you are happy with it, comment it out and replace it with Bindable / Commandable AppBar of your choice.

The whole experience has been such joy that instead of coding till the middle of the night, I switch the laptop off after about 30 – 45 mins of mucking about.

MVVM and me – Part 3

Today is continuation of yesterday’s post.

Yesterday my goal was to find out how one goes about setting static page strings in MVVM. There are many ways.

  1. Hard code it directly in xaml
  2. Define resources in App.xaml
  3. Use Resource Dictionary

I have used ResourceDictionary a lot in past when dealing with Localization and I think that is the only right way to set xaml strings. That takes care of present and future need without being too bulky

By default my WP8 project added support for Localisation, It created Resource folder with AppResources.resx, LocalizedString class that exposes the resources and finally set it as a resource in App.xaml.

proj-localization
AppResources

All you then do is set the strings like this

<TextBlock Text="{Binding LocalizedResources.MainViewChoiceText, Source={StaticResource LocalizedStrings}}" Margin="12, 0"/>

All sorted. And in case you decide to localise your apps, all you need to do is add more resx files.