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

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

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.

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!

Updated PersistHelper for #Win8Dev #WinRT

A while back I posted some persistence helper class. It was hardcoded to use LocalFolder and LocalSettings. I eventually updated it. Now it requires you the developer to specify a folder and settings object. Hope it is helpful.

public class PersistHelper
{
    //private static StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.RoamingFolder;
    //private static ApplicationDataContainer storageSettings = ApplicationData.Current.LocalSettings;

    public async static Task<bool> FileExisits(StorageFolder folder, string fileName)
    {
        try
        {
            StorageFile file = await folder.GetFileAsync(fileName);
            return file != null;
        }
        catch
        {
            return false;
        }
    }

    private async static Task<StorageFile> GetFileIfExistsAsync(StorageFolder folder, string fileName)
    {
        try
        {
            return await folder.GetFileAsync(fileName);
        }
        catch
        {
            return null;
        }
    }


    public static async Task<T> LoadObjectFromStorage<T>(Windows.Storage.StorageFolder storageFolder)
    {
        T ObjToLoad = default(T);

        try
        {
            StorageFile storageFile = await storageFolder.CreateFileAsync(GetFileName<T>(), 
                CreationCollisionOption.OpenIfExists);

            using (Stream inStream = await storageFile.OpenStreamForReadAsync())
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                ObjToLoad = (T)serializer.Deserialize(inStream);
            }
        }
        catch (Exception error)
        {
            throw new NotImplementedException(error.Message);
        }

        return ObjToLoad;
    }

    public static async void SaveObjectToStorage<T>(T ObjectToSave, Windows.Storage.StorageFolder storageFolder)
    {
        string filename = GetFileName<T>();
                
        using (Stream fs = await storageFolder.OpenStreamForWriteAsync(filename, CreationCollisionOption.ReplaceExisting))
        {
            using (StreamWriter sw = new StreamWriter(fs))
            {
                XmlSerializer ser = new XmlSerializer(typeof(T));
                ser.Serialize(sw, ObjectToSave);
            }
        }
    }

    public static string GetFileName<T>()
    {
        return typeof(T).FullName + ".xml";
    }

    public async static Task<bool> IsObjectPersisted<T1>(Windows.Storage.StorageFolder storageFolder)
    {
        string file = GetFileName<T1>();

        StorageFile storageFile = await GetFileIfExistsAsync(storageFolder, file);

        return (storageFile != null);
    }

    public static T LoadSetttingFromStorage<T>(string Key, Windows.Storage.ApplicationDataContainer storageSettings)
    {
        T ObjToLoad = default(T);

        if (storageSettings.Values.ContainsKey(Key))
        {
            using (StringReader sr = new StringReader((string)storageSettings.Values[Key]))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                ObjToLoad = (T)serializer.Deserialize(sr);
            }
        }

        return ObjToLoad;
    }

    public static void SaveSettingToStorage(string Key, object Setting, Windows.Storage.ApplicationDataContainer storageSettings)
    {
        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            XmlSerializer ser = new XmlSerializer(Setting.GetType());
            ser.Serialize(sw, Setting);
        }

        if (!storageSettings.Values.ContainsKey(Key))
        {
            storageSettings.Values.Add(Key, sb.ToString());
        }
        else
        {
            storageSettings.Values[Key] = sb.ToString();
        }
    }

    public static bool IsSettingPersisted(string Key, Windows.Storage.ApplicationDataContainer storageSettings)
    {
        return storageSettings.Values.ContainsKey(Key);
    }
}

HexColor for use with #wpdev and #win8dev

I found this code a while back and its been very handy at times. I have no idea where I found it so I apologise for not attributing it to the writer. Hope this is helpful to others as it has been to me.

public struct HexColour
{
    // Regex: This pattern matches hex codes in these two formats:
    // #000000 (no alpha value) and #FF000000 (alpha value at front).
    const string HEX_PATTERN = @"^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{8})$";

    const int LENGTH_WITH_ALPHA = 8;

    Color _color;

    public HexColour(string hexCode)
    {
        if (hexCode == null)
        {
            throw new ArgumentNullException("hexCode");
        }

        if (!Regex.IsMatch(hexCode, HEX_PATTERN))
        {
            throw new ArgumentException("Format must be #000000 or #FF000000 (no extra whitespace)", "hexCode");
        }

        // shave off '#' symbol
        hexCode = hexCode.TrimStart('#');

        // if no alpha value specified, assume no transparency (0xFF)
        if (hexCode.Length != LENGTH_WITH_ALPHA)
            hexCode = String.Format("FF{0}", hexCode);

        _color = new Color();
        _color.A = byte.Parse(hexCode.Substring(0, 2), NumberStyles.AllowHexSpecifier);
        if (_color.A < 50)
            _color.A = 50;
        _color.R = byte.Parse(hexCode.Substring(2, 2), NumberStyles.AllowHexSpecifier);
        _color.G = byte.Parse(hexCode.Substring(4, 2), NumberStyles.AllowHexSpecifier);
        _color.B = byte.Parse(hexCode.Substring(6, 2), NumberStyles.AllowHexSpecifier);
    }

    public byte A
    {
        get { return _color.A; }
        set { _color.A = value; }
    }

    public byte R
    {
        get { return _color.R; }
        set { _color.R = value; }
    }

    public byte G
    {
        get { return _color.G; }
        set { _color.G = value; }
    }

    public byte B
    {
        get { return _color.B; }
        set { _color.B = value; }
    }

    // Implicit cast from HexColor to Color
    public static implicit operator Color(HexColour hexColor)
    {
        return hexColor._color;
    }

    // Implicit cast from Color to HexColor
    public static implicit operator HexColour(Color color)
    {
        HexColour c = new HexColour();
        c._color = color;
        return c;
    }

    // Just like with Color, ToString() prints out the hex value of the
    // color in #ARGB format (example: #FF000000) by default.
    public override string ToString()
    {
        return ToString(true);
    }

    // I don't always need the alpha value, so I added an overload here
    // that lets me return the hex value in #RBG format (example: #000000).
    public string ToString(bool includeAlpha)
    {
        if (includeAlpha)
        {
            return _color.ToString();
        }
        else
        {
            return String.Format("#{0}{1}{2}", _color.R.ToString("x2"), _color.G.ToString("x2"), _color.B.ToString("x2"));
        }
    }
}

Happy coding

Async Task mechanism and OnNavigatedTo Part 2 #win8dev

Previously i discussed issues i had with #win8dev and #wpdev and using async task mechanism – http://invokeit.wordpress.com/2012/10/15/async-await-and-onnavigatedto-wpdev-win8dev/. With #wpdev you have to consume that mechanism as long as you use async CTP.

So how does one go about doing it in #win8dev #winrt correctly ? We start by marking all methods as async (in this case OnNavigatedTo. If we were firing mutilple calls and possibly making UI calls, #win8dev async implementations is a breeze.

AsyncDataSourceObj aeo = new AsyncDataSourceObj();
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    bErrored = false;
    try
    {
        MyDataType1 mydata1 = await aeo.UseAsyncAwaitStyleCall1();
        MyDataType2 mydata2 = await aeo.UseAsyncAwaitStyleCall2();
        MyDataType3 mydata3 = await aeo.UseAsyncAwaitStyleCall3();
    }
    catch
    {
        bErrored = true;
    }

    if(bErrored)
    {
        await (new MessageDialog("Error")).ShowAsync();
    }
    else
        // process data however you want.
}

above however isn’t a very good implementation as it chains various tasks. you dont want to do this unless the second call depends on the first etc. so if you have distinct calls, one can do this

AsyncDataSourceObj aeo = new AsyncDataSourceObj();
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    bErrored = false;
    try
    {
        List mytasklist = new List();
        mytasklist.Add(aeo.UseAsyncAwaitStyleCall1());
        mytasklist.Add(aeo.UseAsyncAwaitStyleCall2());
        mytasklist.Add(aeo.UseAsyncAwaitStyleCall3());

        await Task.WhenAll(mytasklist);
    }
    catch
    {
        bErrored = true;
    }

    if(bErrored)
    {
        await (new MessageDialog("Error")).ShowAsync();
    }
    else
        // process data however you want.
    // process data however you want.
}

the idea as explained to me by Oren after being told off by Matt is that you leave it to underlying implementation to use whatever it has rather than hacking around with it.

the code as a result is cleaner and works just as well :) What exactly does WhenAll do.. well it asks async task implementation to continue execution when all the tasks in the list are completed. The good part is that its a non blocking call. There’s also WhenAny which continues execution when one of the tasks in the list has completed.

Try and avoid WaitAny or WaitAll as both are blocking calls.

In case of #wpdev it implementation with async ctp was basic and one has to use hacks unforunately. we shall see what Async Targeting Pack brings to #wpdev party in a bit

Just for the record, you might not want to just chuck the exception away.. you should try and log it for future reference as it could be an issue thats just surfacing. have a look at MetroLog on nuget or if you like source, go github

Embed / use custom font in Windows 8 Store apps #win8dev #winrt

I was supposed to blog earlier but forgot. Saw a question on Stackoverflow and thought.. now is the time. So how does one embed fonts ?
Well just include them in project as Content files.

How do you use them.. here is how

<ComboBox x:Name="cbFont" Width="230" Margin="0, 10, 0, 0" Foreground="Black" Background="Gray" SelectionChanged="cbFont_SelectionChanged">
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/akka.ttf#Akka" />
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/alghorie neue.ttf#alghorie neue" />
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/Alpha Mutation.ttf#Alpha Mutation" />
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/AlphaFlowers.ttf#AlphaFlowers" />
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/AMSTRG__.TTF#Ams Trame" />
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/ARCADE_I.TTF#Arcade Interlaced" />
     <ComboBoxItem Content="12:34" FontFamily="Assets/Fonts/ARCADE_R.TTF#Arcade Rounded" />
</ComboBox>

You dont have to use combo box you can use anything.. i use it with TextBlock etc as well. Just remember. After the font file name, you need to put the font name itself. Thats all

Porting Windows Phone App to Windows 8 #wpdev #win8dev

The main message of HHGTG was “Don’t Panic”. It’s universally applicable and the case for porting you WP app to Windows 8 is no different. As WP Devs, we are already used to restrictions on what can and cannot be done. Windows 8 is a different beast that uses the same idea but being a full-blown OS, APIs exposed are different.

Windows Phone API is Silverlight API. Windows 8 has native support for XAML & HTML5. The API available to developers are WinRT and can be used from C++/CX, .NET and JS. I am going to discuss porting a C# Silverlight Windows Phone App to Windows 8. There are two parts to porting story. Get the code working and adapting the user interface. Getting the code working is the easy part. Adapting the UI slightly a bit tricky. Let’s get started

Code Porting – the easy way

Like many, I prefer to do just about everything the easy way. How do I go about it ? Well I take chunks of code and I move it between projects.

1) Custom Controls:

If you use custom controls, you might have to port some of them. A lot of controls have equivalents. If you use ListPicker or LongListSelector from Silverlight Toolkit, try ComboBox. I used AutoCompleteBox and had to write my own. If source is available, porting control is easy. In another project, I ported coding 4 fun toolkits Hexagon Color Picker.

2) XAML:

Start with the basic grid and build your xaml by pulling chunks from Windows Phone project. Most xaml with exception of certain controls can be copied. I have migrated storyboard animations etc no issues.

3) C# code:

    1. WinRT development like WP Dev requires use of asynchronous mechanism however the native WinRT API prefers to use async Task mechanism. Any methods making system calls need to be async and APIs need to be waited for.
      Read more about async programming on msdn http://msdn.microsoft.com/en-us/library/hh191443.aspx
    2. Storage:
      •   IsolatedStorageFile
      •   IsolatedStorageSettings
      public static void GetLevelData(DictionaryDef dict, int Level)
      {
          if (dict != DictionaryDef.NotSet)
          {
              App.WordList.Clear();
              App.WordDict.Clear();
      
              string File = GetLevelFile(dict, Level);
      
              Uri uriPaths = new Uri(File, UriKind.RelativeOrAbsolute);
      
              StreamResourceInfo sriPaths = System.Windows.Application.GetResourceStream(uriPaths);
      
      
              using (StreamReader sr = new StreamReader(sriPaths.Stream))
              {
                  string l;
                  while ((l = sr.ReadLine()) != null)
                  {
                      if (!String.IsNullOrWhiteSpace(l))
                      {
                          App.WordList.Add(l);
                          App.WordDict.Add(l, 0);
                      }
                  }
              }
          }
      }
      
      public class PersistHelper
      {
          public static T LoadObjectFromStorage<T>()
          {
              T ObjToLoad = default(T);
      
              try
              {
                  using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
                  {
                      if (isf.FileExists(GetFileName<T>()))
                      {
                          using (IsolatedStorageFileStream fs = isf.OpenFile(GetFileName<T>(), System.IO.FileMode.Open))
                          {
                              XmlSerializer ser = new XmlSerializer(typeof(T));
                              ObjToLoad = (T)ser.Deserialize(fs);
                          }
                      }
      
                  }
              }
              catch (Exception error)
              {
                  throw new NotImplementedException(error.Message);
              }
      
              return ObjToLoad;
          }
      
          public static void SaveObjectToStorage<T>(T ObjectToSave)
          {
              TextWriter writer;
      
              using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
              {
                  using (IsolatedStorageFileStream fs = isf.OpenFile(GetFileName<T>(), System.IO.FileMode.Create))
                  {
                      writer = new StreamWriter(fs);
                      XmlSerializer ser = new XmlSerializer(typeof(T));
                      ser.Serialize(writer, ObjectToSave);
                      writer.Close();
                  }
              }
          }
      
          public static string GetFileName<T>()
          {
              return typeof(T).FullName + ".xml";
          }
      
          public static bool IsObjectPersisted<T1>()
          {
              using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
              {
                  return isf.FileExists(GetFileName<T1>());
              }
          }
      
          public static T LoadSetttingFromStorage<T>(string Key)
          {
              T ObjToLoad = default(T);
      
              if (IsolatedStorageSettings.ApplicationSettings.Contains(Key))
              {
                  ObjToLoad = (T)IsolatedStorageSettings.ApplicationSettings[Key];
              }
      
              return ObjToLoad;
          }
      
          public static void SaveSettingToStorage(string Key, object Setting)
          {
              if (!IsolatedStorageSettings.ApplicationSettings.Contains(Key))
              {
                  IsolatedStorageSettings.ApplicationSettings.Add(Key, Setting);
              }
              else
              {
                  IsolatedStorageSettings.ApplicationSettings[Key] = Setting;
              }
          }
      
          public static bool IsSettingPersisted(string Key)
          {
              return IsolatedStorageSettings.ApplicationSettings.Contains(Key);
          }
      }
      
      •   StorageFile / StorageFolder.
      •   Get access to .NET streams using above
      •   Package.Current.InstalledLocation.Path – for   reading content files
      •   ApplicationData.Current provides LocalFolder,   RoamingFolder & TempFolder
      •   ApplicationData.Current provides LocalSettings   & RoamingSettings
      •   RoamingFolder / RoamingSettings shared between   devices.
      public static async void GetLevelData(DictionaryDef dict, int Level)
      {
          if (dict != DictionaryDef.NotSet)
          {
              App.WordDict.Clear();
      
              string File = Path.Combine(Package.Current.InstalledLocation.Path, GetLevelFile(dict, Level));
      
              StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(Path.GetDirectoryName(File));
      
              using (Stream s = await folder.OpenStreamForReadAsync(Path.GetFileName(File)))
              {
                  using (StreamReader sr = new StreamReader(s))
                  {
                      string l = null;
                      while (true)
                      {
                          l = sr.ReadLine();
                          if (l == null)
                              break;
      
                          if (l.Length > 0)
                              App.WordDict.Add(l, null);
                      }
                  }
              }
          }
      }
      
      public class PersistHelper
      {
          //private static StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.RoamingFolder;
          //private static ApplicationDataContainer storageSettings = ApplicationData.Current.LocalSettings;
      
          private async static Task GetFileIfExistsAsync(StorageFolder folder, string fileName)
          {
              try
              {
                  return await folder.GetFileAsync(fileName);
      
              }
              catch
              {
                  return null;
              }
          }
      
      
          public static async Task LoadObjectFromStorage(Windows.Storage.StorageFolder storageFolder)
          {
              T ObjToLoad = default(T);
      
              try
              {
                  StorageFile storageFile = await storageFolder.CreateFileAsync(GetFileName(),
                      CreationCollisionOption.OpenIfExists);
      
                  using (Stream inStream = await storageFile.OpenStreamForReadAsync())
                  {
                      XmlSerializer serializer = new XmlSerializer(typeof(T));
                      ObjToLoad = (T)serializer.Deserialize(inStream);
                  }
              }
              catch (Exception error)
              {
                  throw new NotImplementedException(error.Message);
              }
      
              return ObjToLoad;
          }
      
          public static async void SaveObjectToStorage(T ObjectToSave, Windows.Storage.StorageFolder storageFolder)
          {
              string filename = GetFileName();
      
              using (Stream fs = await storageFolder.OpenStreamForWriteAsync(filename, CreationCollisionOption.ReplaceExisting))
              {
                  using (StreamWriter sw = new StreamWriter(fs))
                  {
                      XmlSerializer ser = new XmlSerializer(typeof(T));
                      ser.Serialize(sw, ObjectToSave);
                  }
              }
          }
      
          public static string GetFileName()
          {
              return typeof(T).FullName + ".xml";
          }
      
          public async static Task IsObjectPersisted(Windows.Storage.StorageFolder storageFolder)
          {
              string file = GetFileName();
      
              StorageFile storageFile = await GetFileIfExistsAsync(storageFolder, file);
      
              return (storageFile != null);
          }
      
          public static T LoadSetttingFromStorage(string Key, Windows.Storage.ApplicationDataContainer storageSettings)
          {
              T ObjToLoad = default(T);
      
              if (storageSettings.Values.ContainsKey(Key))
              {
                  using (StringReader sr = new StringReader((string)storageSettings.Values[Key]))
                  {
                      XmlSerializer serializer = new XmlSerializer(typeof(T));
                      ObjToLoad = (T)serializer.Deserialize(sr);
                  }
              }
      
              return ObjToLoad;
          }
      
          public static void SaveSettingToStorage(string Key, object Setting, Windows.Storage.ApplicationDataContainer storageSettings)
          {
              StringBuilder sb = new StringBuilder();
              using (StringWriter sw = new StringWriter(sb))
              {
                  XmlSerializer ser = new XmlSerializer(Setting.GetType());
                  ser.Serialize(sw, Setting);
              }
      
              if (!storageSettings.Values.ContainsKey(Key))
              {
                  storageSettings.Values.Add(Key, sb.ToString());
              }
              else
              {
                  storageSettings.Values[Key] = sb.ToString();
              }
      
          }
      
          public static bool IsSettingPersisted(string Key, Windows.Storage.ApplicationDataContainer storageSettings)
          {
              return storageSettings.Values.ContainsKey(Key);
          }
      }
      
    3. Playing Sounds
    4. Silverlight MediaElement
      XNA MediaPlayerSoundEffects
      XAML MediaElement
      MonoGame   (XNA) MediaPlayerSoundEffects
      SharpDX Managed   DirectX wrappers for C#
    5. Behaviours:
    6. No built in support for behaviours (unlike WP Blend support through Interactions lib)

      http://winrtbehaviors.codeplex.com provides DragFlickBehavior

    7. Navigation:
      • Use Frame class for navigation in WinRT. Exposes methods similar to those of NaviationService.
      • Frame does not support AddBackEntry and RemoveBackEntry
      • No Back button and associated event. Add backbutton or consume a xaml page other than Basic Page (all other pages derive from LayoutAwarePage) and a back button is added and is automatically bound to correct method.
    8. NavigationService.Navigate(new Uri("/GamePlay.xaml", UriKind.Relative));
      
      if(NavigationService.CanGoBack)
          NavigationService.GoBack();
      
      Frame.Navigate(typeof(GamePlay));
      
      if(Frame.CanGoBack)
          Frame.GoBack();
      

      Designing for Windows 8

      This is the difficult part and you get the hang of this after a few AEL sessions. Windows 8 app is more than just a port of Windows Phone app.

      1)      The screen real estate is only for your apps real consumer content.

      2)      Settings Pane is a single place to hold things relating to app configuration. App config, in app purchase into, privacy policy, about etc. all these are exposed by Settings Pane.

      3)      Use Settings Flyout (Callisto lib) to link individual item to Xaml content.. e.g. settings flyout to display app config.

      4)      Windows 8 apps do more than Portrait and Landscape modes. They have snapped and filled modes as well.

      5)      There is life outside Pivot and Panorama controls. Use FlipView, Grid, GridView, Semantic zoom etc so expose your content in the best way possible.

      6)      Take your time. Porting the 1st app is always difficult – it gets a lot easier going forward.