Thursday, 1 March 2012

WP7 Bing Maps Crash while being led down the garden path!

There I was, innocently trying to data bind to my collection of objects, and use an item template to render them on my maps control for an upcoming application.

So, I added a layer to my map like this:

<my:MapItemsControl x:Name="tracksLayer"
ItemsSource="{Binding Tracks }"
ItemTemplate="{StaticResource TrackTemplate}" />



and implemented my data template like this in the page resources:


<SolidColorBrush x:Key="FadedBrush" Color="Blue" Opacity=".5" />

<DataTemplate x:Name="TrackTemplate">
<MapPolyline Locations="{Binding Locations}"
Stroke="{StaticResource FadedBrush}"
StrokeThickness="5" />
</DataTemplate>



That all worked fine, and I got my brush displayed.


So, I thought great, now lets ask the track what color it should be, and changed the stroke definition to look like this:


<DataTemplate x:Name="TrackTemplate">
<my:MapPolyline Locations="{Binding Locations}"
Stroke="{Binding TrackBrush}"
StrokeThickness="5" />
</DataTemplate>



To my surprise, this threw an exception (or two) and terminated the program.


After a couple hours of debugging to figure out what had happened and being led down the garden path by the StaticResource, I eventually came across the fact that Stroke and StrokeThickness are not dependency properties in MapShapeBase.


Luckily, someone else had solved the problem here.  But be careful, the declarations there aren’t marked public and the dependency properties are implemented differently in WPF, so it won’t work out of the box.


I’ve attached my class here updated for WP7:


public class MapPolylineBindable : MapPolyline
{
public static readonly DependencyProperty StrokeProperty;
public static readonly DependencyProperty StrokeThicknessProperty;

static MapPolylineBindable()
{
MapPolylineBindable.StrokeProperty = DependencyProperty.Register("Stroke", typeof(Brush), typeof(MapPolylineBindable),
new PropertyMetadata(null, OnStrokeExChanged));

MapPolylineBindable.StrokeThicknessProperty = DependencyProperty.Register("StrokeThickness", typeof(double), typeof(MapPolylineBindable),
new PropertyMetadata(1.0, OnStrokeThicknessExChanged));
}

public new Brush Stroke
{
get { return (Brush)GetValue(MapPolylineBindable.StrokeProperty); }
set { SetValue(MapPolylineBindable.StrokeProperty, value); }
}

private static void OnStrokeExChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MapPolyline).Stroke = (Brush)e.NewValue;
}

public new double StrokeThickness
{
get { return (double)GetValue(MapPolylineBindable.StrokeThicknessProperty); }
set { SetValue(MapPolylineBindable.StrokeThicknessProperty, value); }
}

private static void OnStrokeThicknessExChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MapPolyline).StrokeThickness = (double)e.NewValue;
}
}

and to bind, you need to do this:

 


<DataTemplate x:Name="TrackTemplate">
<hack:MapPolylineBindable Locations="{Binding Locations}"
Stroke="{Binding TrackBrush}"
StrokeThickness="5" />
</DataTemplate>


Friday, 24 February 2012

IsolatedStorage and Thread Safety

In the documentation of the IsolatedStorageFile class, Microsoft say this:

Any instance members are not guaranteed to be thread safe.

Normally, members not being guaranteed to be thread safe means that when you have an object, the object cannot be shared across threads and have members called from more than one thread.

I suspect either that is not what is intended, or there’s a serious bug.

If you’ve followed my two previous posts today, you’ll notice that I’ve been trying to read from IsolatedStorage on a ThreadPool thread, and was debating whether I needed to create a separate IsolatedStorageFile for each thread.

Interepreting the statement in the documentation strictly implies that I should call GetUserStoreForApplication to get a new instance whenever I want to examine isolated storage.

Now, imagine my surprise when I fire up more than one thread pool thread, and get an exception when calling GetFileNames with a set of non-overlapping folders as follows:

System.IO.IsolatedStorage.IsolatedStorageException

An error occurred while accessing IsolatedStorage.at System.IO.IsolatedStorage.IsolatedStorageFile.EnsureStoreIsValid()
at System.IO.IsolatedStorage.IsolatedStorageFile.GetFileNames(String searchPattern)
at App.Model.DaySummary.BackgroundProcess()
at Microsoft.Phone.Reactive.Observable.<>c__DisplayClassa0.<>c__DisplayClassa2.<ToAsync>b__9f()
at Microsoft.Phone.Reactive.ThreadPoolScheduler.<>c__DisplayClass1.<Schedule>b__0(Object _)
at System.Threading.ThreadPool.WorkItem.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadPool.WorkItem.doWork(Object o)
at System.Threading.Timer.ring()

So it now looks like I need to protect this with some kind of lock to prevent more than one object accessing any aspect of IsolatedStorage at a time.  Adding a lock around the creation of the IsolatedStorage object certainly prevents the error.

Addendum

Well, it looks like I had a bug.  When I rewrote my wrapper class for getting the IsolatedStorageFile via GetUserStoreForApplication, I inadvertently forgot to remove the static keyword from the variable holding it.  As a result, I was using the same object across threads.

In that situation, as you would expect, the object failed, as it is not guaranteed thread safe.  Removing the static keyword is all it took to fix the problem, so for the moment it looks like separate instances can be used at will on a single thread without regard to what else is accessing isolated storage (from a thread safety point anyway).

update March 1 2012

Quite a lot of discussion has happened on the AppHub forums on this since it was originally written, concerning thread safety of the entire platform.

If you’re interested, the whole discussion is here.

Asynchronous actions with RX

I was trying to come up with a syntactically tidy way to run some code in the ThreadPool.

Assume I have a function as follows that performs the work:

void PopulateIsolatedStorageList()
{
using (iso = IsolatedStorageFile.GetUserStoreForApplication())
{
string[] found = iso.ISO.GetDirectoryNames("Logs/*.*");
foreach (string s in found)
{
// do some time consuming processing here
}
}
}


First I attempted to create an Action delegate for the above, and then tried to do this:


Action b = this.PopulateIsolatedStorageList;
Observable.FromAsyncPattern(b.BeginInvoke, b.EndInvoke )().
ObserveOn(Scheduler.ThreadPool).Subscribe();



This did not work.  First, PopulateIsolatedStorageList ended up with a null this pointer.


Secondly, it seemed to run on the main thread in any case.





A bit of lateral thinking required


What are my goals?



  1. I want to run my action on a worker thread.

  2. I want to perform some completed action on the UI thread (namely OnPropertyChanged).

Why not create an enumeration of things I want to get done on the worker thread then?


Action[] a = { PopulateIsolatedStorageList};
a.ToObservable(Scheduler.ThreadPool).Subscribe
( action => action(),
      () => Deployment.Current.Dispatcher.BeginInvoke(() => UINotify() ));


Where UINotify() is the expression to perform on the main thread.

 

And surprisingly, it all works.

Update – It helps if you find the right method


Having searched some more I found another mechanism, which removes the need to create the array, and is probably the proper way to do it.


Observable.ToAsync(PopulateIsolatedStorageList)()
.Subscribe(
     _ => { },
() => Deployment.Current.Dispatcher.BeginInvoke(() => UINotify() ));


IsolatedStorage performance tests on WP7

The documentation on IsolatedStorage seems to be incomplete on the subject of when you should open and close the ApplicationStore, particularly since the only the public static members are thread safe.

It would appear that the best performance might be to open the IsolatedStorage using IsolatedStorageFile.GetUserStoreForApplication() once for the application, and share the instance, but what operations are allowed across multiple threads if you do?  Well, that isn’t clear.

So, the safest thing to do is to open one every time you need it, and dispose of it when finished.

I’ve already verified that each time you call GetUserStoreForApplication, a different object is returned, and built a harness that does this:

isf = IsolatedStorageFile.GetUserStoreForApplication();

int v = System.Environment.TickCount;

for (int i = 0;i<1000;i++)
{

CheckFolderBare("Shared");
}

int v2 = System.Environment.TickCount - v;
v = System.Environment.TickCount;

for (int i= 0;i<1000;i++)
{
CheckFolderSlow("Shared");
}
int v3 = System.Environment.TickCount - v;

MessageBox.Show(string.Format("Fast: {0}ms\nSlow {1}ms", v2, v3));


My Implementations of CheckFolderBare and CheckFolderSlow are this:


private bool CheckFolderBare(string s)
{
return isf.DirectoryExists(s);
}

private bool CheckFolderSlow(string s)
{
using (var isf2 = IsolatedStorageFile.GetUserStoreForApplication())
{
return isf2.DirectoryExists(s);
}
}

 

Not surprisingly, my initial suspicions were correct.  It isn’t particularly cheap to create and dispose of the file (taking about 1.3ms).  Timings were:

Fast: 2787ms

Slow: 4086ms

 

How about enumerating the folders?  To do this, I create a folder in IsolatedStorage named Logs, and created in that folder 7 additional folders, and then ran these functions 1000 times:

private string[] FoldersSlow()
{
using (var isf2 = IsolatedStorageFile.GetUserStoreForApplication())
{
return isf2.GetDirectoryNames("Logs/*.*");
}
}

private string[] FoldersFast()
{
return isf.GetDirectoryNames("Logs/*.*");
}



Results differed by about 2.5ms:


Fast: 7619ms
Slow: 9100ms


So, to make things more interesting, I decided to create and delete a folder on each operation, so on odd calls, I remove the folder Logs/Bogus and on even calls I created it.


Surprisingly, I ended up with times differing by 1.3ms, and in some cases, not showing any difference at all.


Fast: 16406ms
Slow: 17542ms


Verdict?


On balance, I’d say there is no real performance penalty by opening and closing the IsolatedStorage in a realistic usage scenario.

Monday, 6 February 2012

HTC Surround Windows Phone 7 takes terrible photos

I took my phone away this weekend, and since it has a 5MP camera, I thought I’d take a picture while in Wales away from the snow.

Here’s how it turned out:

WP_000047

Admittedly I zoomed in a couple of times, but this is horrible.  If you click the above image, you can see the full horror of it.  The image for some reason came out at 686KB. 

I have a Canon A400, that is 3.2 MP and it’s images are usually on the order of 1.0MB.

I’m currently trying to find out why the compression effects are so bad.

Friday, 20 January 2012

WP7 Application crash (in buy now!)

I followed the advice on msdn about how to add a buy now button to my Windows phone app.

Today, I noticed I had a crash in one of our applications, and downloaded the stack trace.

On the stack is MarketplaceDetailTask.Show

A bit of searching and trial and error shows that if the user executes this quickly (by double tapping the button the handler is on) the program will crash.

I’m guessing the user didn’t buy.

Follow up:

To solve this, I added the following belt and braces code:

private void OnPurchase(object sender, RoutedEventArgs e)
{
btnbuynow.IsEnabled = false;
try
{
_marketPlaceDetailTask.Show();
}
catch (InvalidOperationException ex)
{
}
}



This disables the button which prevents subsequent button presses while transitioning.  I catch the exception thrown for good measure, though as no type of exception is documented to be raised, maybe I should just catch everything.


I then reenable the buy now button whenever the page is navigated to as follows:


protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
btnbuynow.IsEnabled = true;
base.OnNavigatedTo(e);
}

Wednesday, 11 January 2012

ScrollViewer Inertia and ManipulationCompleted

Today, I was solving a problem with a ScrollViewer.  I want to simulate a continuously scrolling view of the world for our application “Terminator

I was attempting to handle the scrolling stop in the ManipulationCompleted event handler.  The MSDN documentation on ManipulationCompleted states “Occurs when a manipulation and inertia on the UIElement is complete.”

This is clearly not the case, as it appears to fire when the direct manipulation stops, but the inertia hasn’t.

Luckily there’s a way to check when scrolling stops on Peter Torr’s blog.

In addition, I had hoped to be able to utilize ManipulationDelta to find out where the scroll viewer was now.  This also was a dead end, wasting hours of work.  My requirement was to be notified of changes to HorizontalOffset on the ScrollViewer.

Alas, there is no event raised for that, however, I came across this useful code on Beat Kiener’s blog to Listen to DependencyProperty changes on WP7 and Silverlight.

With that, I can now know the current position (when Mango decides to update it) of my scroll bar, and react accordingly.

How can it be this difficult?  When apps only bring in tiny amounts of money, the inordinate effort to polish off an app seems completely disproportionate to the reward for doing it right.  Not to mention the amount of research that needs to go in to find all of these little fixes.