WinRT and pointer manipulation #win8dev #winrtdev

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Happy coding

Super Duper All in one VisibilityConverter for #wpdev

On the 4th of July I blogged about simple visibility converters to toggle visibility. Of course I used two converters then.. one Visibility and another inverse aka Invisibility.

I also said that some clever people might tell me that its possible to do both in a single converter. Whilst that is true, no one did so apparently no one really reads or cares :| Coding4Fun toolkit has a converter than exposes Inverted property but my idea was to be able to use the same converter by pass a parameter.

So here’s what I did

 

public class VisibilityConverter : IValueConverter
{
    public enum Mode
    {
        Default,
        Inverted,
    }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Visibility returnVisibility = Visibility.Visible;
        Mode mode = Mode.Default;
        try
        {
            if(parameter != null)
                mode = (Mode)Enum.Parse(typeof(Mode), (string)parameter, true);
        }
        catch 
        {
            mode = Mode.Default;
        }

        if (value == null)
        {
            returnVisibility = Visibility.Collapsed;
        }
        else if(value is bool)
        {
            bool bVal = (bool)value;
            if (!bVal)
                returnVisibility = Visibility.Collapsed;
        }
        else if (value is string)
        {
            string itemVal = value as String;

            if (String.IsNullOrWhiteSpace(itemVal))
                returnVisibility = Visibility.Collapsed;
        }
        else if (value is IList)
        {
            IList objectList = value as IList;
            if (objectList == null || objectList.Count == 0)
                returnVisibility = Visibility.Collapsed;    
        }

        if (mode == Mode.Inverted)
            return returnVisibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
        else
            return returnVisibility;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

and how do I use it ? well this is how

<TextBlock Text="{Binding Review}" Visibility="{Binding Review, Converter={StaticResource VisibilityConverter}}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"/>
<TextBlock Text="(rating only)" Visibility="{Binding Review, Converter={StaticResource VisibilityConverter}, ConverterParameter=Inverted}" Foreground="{StaticResource PhoneSubtleBrush}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"/>

I could pass Default parameter or just leave it out and it defaults to normal behaviour. If I however pass Inverted parameter to the converter, it inverts the behaviour. Bingo.

Now waiting for the next clever people to tell me that I am using parameter designed for something else :)

LongListSelector and scroll to top #wpdev

I pulled out my Marauder’s Map, tapped my wand and said “I solemnly swear I’m up to no good”. Bang Visual Studio opens up and I am back to playing with Cineworld app. I know a few users hate my ever so frequent updates but I use the app and I need change :)

so I recently got to putting a DatePicker and LongListSelector for being able to “Show by date” film listings. It all worked – well mostly worked until I realised that LongListSelector was scrolled all the way to the bottom. I wonder why..

Of course there is no SelectedIndex I could magically set so did a quick search on the net.. StackOverflow usually has all the answers to “the life, the universe and everything else” but it didn’t really have one for this question.

There was a note saying use UpdateLayout and then ScrollTo and setting it to ItemSource’s first child did the trick for WP8 but not for WP7.

var dataLetter = cd.GetGroupForDate(userSelection); // get the grouped list of films

FilmsForSelectedDate.Clear(); // clear the bound observable collection

foreach (var entry in dataLetter)
    FilmsForSelectedDate.Add(entry);

lstShowByDate.UpdateLayout(); // call updatelayout

if (FilmsForSelectedDate.Count != 0)
    lstShowByDate.ScrollTo(FilmsForSelectedDate[0]); // set scroll to first group in the list

The problem I realised was that the first group in the LLS had zero items i.e. it was not being shown. This affected WP7 toolkit LongListSelector but not the native WP8 LongListSelector

var dataLetter = cd.GetGroupForDate(userSelection);

FilmsForSelectedDate.Clear();

foreach (var entry in dataLetter)
    FilmsForSelectedDate.Add(entry);

lstShowByDate.UpdateLayout();

if (FilmsForSelectedDate.Count != 0)
    lstShowByDate.ScrollTo(FilmsForSelectedDate.First(g => g.HasItems));

So the solution as shown above is, find the group that has items and set scroll to that element.

Mischief managed!

Converter to toggle visibility when content is empty #wpdev #wp8dev #wp7dev #win8dev

Recently Gustavo Guerra posted a question on StackOverflow.com stating that behaviour of TextBlock was different on Windows Phone 7 and Windows Phone 8. If the Text is empty, WP7 renders TextBlock with Height set to 0. On WP8, it occupies full height.

I replied back stating that he should consider using a Converter for the same. Of course I had a similar issue with Cineworld app. The screenshot shows ratings & review screen I implemented – its almost a copy of Windows Phone Store ratings & review.
wp_ss_20130704_0002[1]

what I noticed was that if user chose to not review it, the textbox would be fully render. So I did what I suggested Gustavo do. Created two converters – StringLengthInvisibilityConverter and StringLengthVisibilityConverter. Almost identical but doing the opposite thing.

 

public class StringLengthInvisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string itemVal = value as String;
        if (String.IsNullOrWhiteSpace(itemVal))
            return Visibility.Collapsed;
        else
            return Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class StringLengthVisibilityConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string itemVal = value as String;
        if (String.IsNullOrWhiteSpace(itemVal))
            return Visibility.Visible;
        else
            return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
<TextBlock Text="{Binding Review}" Visibility="{Binding Review, Converter={StaticResource InvisibilityConverter}}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"/>
<TextBlock Text="(rating only)" Visibility="{Binding Review, Converter={StaticResource VisibilityConverter}}" Foreground="{StaticResource PhoneSubtleBrush}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"/>

 

Now I know one of you clever person is going to tell me I could have just used one :P Let me tell you this:

  1. I thought of it
  2. I could have searched around
  3. Realised no one really cares
  4. Created two converters and got the job done :)
  5. Submitted update and nap time

.

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

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;
}

#async task and the for loop bug

I have used various flavours of async task implementation.
* WP7 / async CTP / v3 VS 2010
* Win8 / VS 2012
* Azure / VS 2012
* SL / async targetting pack / VS 2012

I blogged about how i had issues with #async on #aszure and funnily enough i wasn’t the only one. As discussed in http://invokeit.wordpress.com/2012/11/07/windows-azure-and-async-task-mechanism-async/ if you use something like this

AsyncDataSourceObj aeo = new AsyncDataSourceObj();

protected async Task BuildFilmDataSet()
{
    Films films = await aeo.GetFilmData();
    foreach(var film in films)
    {
        Cinemas cinemas = await aeo.GetCinemaData(film);

        // do things

        // do more things 
    }
}

It doesn’t work and a little investigation showed me that i wasn’t doing anything wrong.

The moment await aeo.GetCinemaData(film); gets called, the execution instead of awaiting moves to the next iteration of for loop. Why in the world you would want that ? I am sure if you wanted that one would have used Parallel.For instead.

For some reason, the creators thought that we clever people wouldn’t ever want to do a async call in for loop. I mean its not like its the most common loop usage etc…

The suggestion is to use Task.Yield() (or TaskEx.Yield) within for loop

It however doesn’t always work. I have tried it with my last Silverlight + Async Targetting pack and its still the same. Infact I had to revert to event based async in that case.

Here’s a screen of azure sample in action