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

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.

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.

MVVM and me – Part 2

I could have done this post yesterday but that would have been a long essay. Today we are going to look at displaying static data on XAML pages.

In C# when you have data that does not change, one tends to use const or static readonly. const causes compiler to replace all occurrences of variable usage with the actual data at compile time. That means you assemblies has no lookups at run-time. Static read-only is a static variable that can also be set in constructor – is read-only otherwise.

Now based on my previous experience doing WinForms and Webforms, normally I tend to set those directly in XAML. Say you want to content of a button or a textblock text (label). So the ideal way would be.

  1. Take View Model
  2. Define consts
  3. Set bindings to consts
  4. Compile and reload

Eureka.. there’s nothing..

Well XAML bindings make a big fuss of INotifyPropertyChanged interface. From sample I know that it works with properties so could it be that ?

Lets start:

  1. Take View Model
  2. Define consts
  3. Define properties
  4. Set property in the constructor to consts
  5. Set bindings to properties
  6. Compile and reload

There.. much better..

Lesson, const and compiler time optimisations are no good.. you need to define properties and additionally call PropertyChanged events and what not including set the properties at VM Creation in order to display data the proper MVVM way. A Single task of setting a text to a textblock or a button is translated into a handful tasks..

Update 1:
My friend @Scottisafool … suggested an alternative approach that he tends to use

  1. Define resources in App.xaml
  2. Set bindings to resources
  3. Compile and reload

This also works.. sure it means that all your strings are in one place but that’s still not MVVM
So it turns out there is not right way but there is a wrong way!! Everything else but code-behind as the last resource is the pukka MVVM way. Wrap up for today I think.

Update 2:
More messages suggesting that the mechanism to declare labels is to either hard code those or use Resources. That’s all fine.. I could use ResourceDictionary just in case the future me might want to localise the app.

That however still leaves the need to support of real consts impossible. There isn’t any support for those in bindings wrt View Model

MVVM and me – Part 1

As you know I recently joined a big name in Mobile Money business as a Senior Windows Developer. Part of that job involves working with others and not just doing WP Dev as a hobbyist (which I still do).

Most WP Devs are inherently XAML devs and they are well versed in MVVM (Model-View-View Model not necessarily in that order). MVVM itself is an extension of MVP (Model View Presenter) which to an extent has similarities to MVC (Model View Controller). Microsoft in .NET 2.0 started extensive work (crusade) against code behind. The idea that has been propagated has been to write less code – its more declarative and less code driven. (Use the system generated code since you are no good at it :) )

Personally I have never been a fan of declarative code. I do like XAML but only the extensible nature of it. how I just about use / contain and create controls with ease. Historically I am a middle-tier / server-side developer who loves not worrying about UI BS. I have spend years making code lean and that means adding layers that do little isn’t my cuppa tea.

However I am developing my next hobbyist app using MVVM. The next few blogs are doing to deal with my experiences moving from code driven to MVVM driven app development.

Before we get started just want to mention the fact that if I wasn’t doing the app in MVVM, I would have already finished it. Right now I am still doing XAML screens and battling myself almost constantly.

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.

PhotoViewer with pinch & zoom, pan and flick support

A while back after scouring the net and merging some code, I created PhotoViewer that is used in my cool camera app. It might not be perfect but it works acceptably. Here’s how it work in code.

Lets start with Xaml..

<Image x:Name="imgViewer"  Stretch="UniformToFill" Width="728" Height="480">
    <Image.RenderTransform>
        <ScaleTransform x:Name="scaleTrans" ScaleX="1" ScaleY="1" />
    </Image.RenderTransform>
    <toolkit:GestureService.GestureListener>
        <toolkit:GestureListener Flick="GestureListener_Flick" 
            PinchStarted="GestureListener_PinchStart" 
            PinchDelta="GestureListener_PinchDelta" 
            PinchCompleted="GestureListener_PinchComplete"
            DragStarted="GestureListener_DragStart"
            DragDelta="GestureListener_DragDelta"
            DragCompleted="GestureListener_DragCompleted"
            Tap="GestureListener_Tap"
            DoubleTap="GestureListener_DoubleTap"/>
    </toolkit:GestureService.GestureListener>
</Image>

as you can see.. we start with Image and add a ScaleTransform. Next up with add GestureService from SL Toolkit. I am going to subscribe to some events. Here’s how the code behind looks

lets start by defining some data members

private readonly DispatcherTimer m_animationTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
private double m_friction = 0.99;
private Point m_scrollStartPoint;
private Point m_scrollTarget = new Point();
private double m_hvelocity;
private double m_vvelocity;
private double m_angle;
private double _cx, _cy;

Next up, set the Tick event in the constructor so we can do something on Tick of the DispatcherTimer.

m_animationTimer.Tick += new EventHandler(m_animationTimer_Tick);

Now the real code.. lets implement some of the event handlers

void m_animationTimer_Tick(object sender, EventArgs e)
{
    if (m_hvelocity < 0.1)
        m_hvelocity = 0;

    if (m_vvelocity < 0.1)
        m_vvelocity = 0;

    if (m_hvelocity == 0 && m_vvelocity == 0)
    {
        m_animationTimer.Stop();
        return;
    }

    if (m_angle > 0 && m_angle < 180)
        scaleTrans.CenterY -= m_vvelocity;
    else
        scaleTrans.CenterY += m_vvelocity;

    if(m_angle > 90 && m_angle < 270)
        scaleTrans.CenterX += m_hvelocity;
    else
        scaleTrans.CenterX -= m_hvelocity;
            
     m_hvelocity *= m_friction;
     m_vvelocity *= m_friction;

     if ((scaleTrans.CenterX <= 0 || scaleTrans.CenterX >= imgViewer.Width) || (scaleTrans.CenterY <= 0 || scaleTrans.CenterY >= imgViewer.Height))
     {
         if (scaleTrans.CenterX < 0)
             scaleTrans.CenterX = 0;

         if (scaleTrans.CenterX > imgViewer.Width)
             scaleTrans.CenterX = imgViewer.Width;

         if (scaleTrans.CenterY < 0)
             scaleTrans.CenterY = 0;

          if (scaleTrans.CenterY > imgViewer.Height)
              scaleTrans.CenterY = imgViewer.Height;

          m_animationTimer.Stop();
    }
}
        
private void GestureListener_Flick(object sender, FlickGestureEventArgs e)
{
    m_angle = e.Angle;

    m_animationTimer.Stop();

    m_scrollStartPoint = e.GetPosition(this);
    m_scrollTarget.X = m_scrollStartPoint.X + e.HorizontalVelocity;
    m_scrollTarget.Y = m_scrollStartPoint.Y + e.VerticalVelocity;
                
    m_hvelocity = Math.Abs(e.HorizontalVelocity)/100;
    m_vvelocity = Math.Abs(e.VerticalVelocity)/100;
                                
    if (m_scrollTarget.X < 0) 
        m_scrollTarget.X = 0;

    if(m_scrollTarget.X > imgViewer.Width)
        m_scrollTarget.X = imgViewer.Width;

    if (m_scrollTarget.Y < 0)
        m_scrollTarget.Y = 0;

    if (m_scrollTarget.Y > imgViewer.Height)
        m_scrollTarget.Y = imgViewer.Height;

    m_animationTimer.Start();
}

private void GestureListener_PinchStart(object sender, PinchStartedGestureEventArgs e)
{
    Point p1 = e.GetPosition(imgViewer, 0);
    Point p2 = e.GetPosition(imgViewer, 1);

    scaleTrans.CenterX = (p1.X + ((p2.X - p1.X) / 2));
    scaleTrans.CenterY = (p1.Y + ((p2.Y - p1.Y) / 2));

    _cx = scaleTrans.ScaleX;
    _cy = scaleTrans.ScaleY;
}

private void GestureListener_PinchDelta(object sender, PinchGestureEventArgs e)
{         
    // Compute new scaling factors
    double cx = _cx * e.DistanceRatio;
    double cy = _cy * e.DistanceRatio;

    // If they're between 1.0 and 4.0, inclusive, apply them
    if (cx >= 1.0 && cx <= 4.0 && cy >= 1.0 && cy <= 4.0)
    {
        if ((cy - 1) < 0.1 && (cx - 1) < 0.1)
            cx = cy = 1;
            
        scaleTrans.ScaleX = cx;
        scaleTrans.ScaleY = cy;
    }
}

private void GestureListener_PinchComplete(object sender, PinchGestureEventArgs e)
{
}

private void GestureListener_DragStart(object sender, DragStartedGestureEventArgs e)
{
}

private void GestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
{
    scaleTrans.CenterX = (scaleTrans.CenterX - e.HorizontalChange);
    scaleTrans.CenterY = (scaleTrans.CenterY - e.VerticalChange);

    if (scaleTrans.CenterX < 0)
        scaleTrans.CenterX = 0;
    else if (scaleTrans.CenterX > (imgViewer.Height * scaleTrans.ScaleX))
        scaleTrans.CenterX = imgViewer.Height * scaleTrans.ScaleX;

    if(scaleTrans.CenterY < 0)
        scaleTrans.CenterY = 0;
    else if (scaleTrans.CenterY > (imgViewer.Height * scaleTrans.ScaleY))
        scaleTrans.CenterY = imgViewer.Height * scaleTrans.ScaleY;
}

private void GestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
{
    scaleTrans.CenterX = (scaleTrans.CenterX - e.HorizontalChange);
    scaleTrans.CenterY = (scaleTrans.CenterY - e.VerticalChange);

    if (scaleTrans.CenterX < 0)
        scaleTrans.CenterX = 0;
    else if (scaleTrans.CenterX > imgViewer.Width)
        scaleTrans.CenterX = imgViewer.Width;

    if (scaleTrans.CenterY < 0)
        scaleTrans.CenterY = 0;
    else if (scaleTrans.CenterY > (imgViewer.Height))
        scaleTrans.CenterY = imgViewer.Height;
}

private void GestureListener_DoubleTap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
    scaleTrans.ScaleX = scaleTrans.ScaleY = 1;
}

private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
    m_animationTimer.Stop();
    m_hvelocity = 0;
    m_vvelocity = 0;
}

Slides and Sample Code from Async Task talk

I have uploaded sample from from my async task talk I did last week on github.
https://github.com/hermitdave/AsyncTask

Here is the slide from the talk – Async Programming.

I started by talking about Event based async.. how framework converted sync WCF methods into event driven async methods when one adds a reference for Silverlight or Winddows Phone or Windows 8 projects. I further explained that if you had multiple dependent calls and how async task might actually help.

Next up for a very simple explanation of async task. how async task code creates a dynamic structure which is used to execute the actual code. I mentioned how async task is run on the same thread the current execution is suspended. This raised many questions.. I tried to unsatisfactorily explain the mechanism (by the looks of it). The idea was to talk through individual samples. So next I fired up App and VS and talked individual samples.

There was a hairy project moment when I was trying to show multi-threading using async task.

I think it was okay.. maybe I should have chosen an easier subject ? maybe I just need to sound more convincing next time. I am also going to have comparable async event samples side by side. There were some comments. 1st one was the same day.. very critical comment suggesting that I knew nothing (as I said async does not result in creation of new threads for execution of the said code). The chap further critisied my samples are overly simple.. and finally my projector skills (issues during the last part of event).

Oh well.. Should I continue talking on the subject ?

Why would you ever use async void

Suppose you have a method that uses async task mechanism. Simplest example would be say displaying MessageDialog. Example I will use is using Launcher class to launch browser with a Url.

string BingMapsUri = String.Format("http://www.bing.com/maps/default.aspx?cp={0}~{1}&lvl=18&style=u", latitude, longitute);
 Uri uri = new Uri(BingMapsUri, UriKind.Absolute);
 await Launcher.LaunchUriAsync(uri);

this will launch the default browser with the uri above. Suppose you had an appbar button and you wanted to use click or tapped event to execute above code. The click event handler by default looks like this

private void btnViewOnMap_Click(object sender, RoutedEventArgs e)
{
}

one way would be to declare btnViewOnMap_Click to return Task and declare it async.

private async Task btnViewOnMap_Click(object sender, RoutedEventArgs e)
{
}

However event handlers do not like this and will refuse to work. In my early Win8 dev days I was stumped by this… what the heck was I supposed to do ? The answer was simple.

private async void btnViewOnMap_Click(object sender, RoutedEventArgs e)
{
}

just add async declaration to the event handler. this just informs the framework that this method will make and await async tasks. the event handler would look like this

private async void btnViewOnMap_Click(object sender, RoutedEventArgs e)
{
    string BingMapsUri = String.Format("http://www.bing.com/maps/default.aspx?cp={0}~{1}&lvl=18&style=u", latitude, longitute);
    Uri uri = new Uri(BingMapsUri, UriKind.Absolute);
    await Launcher.LaunchUriAsync(uri);
}

That is the only use of async void. In all other circumstances, you should use async Task