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.

Friday, 30 December 2011

Tombstoning and Mango: Something’s Awry!

I’ve wasted another day.

I’ve been trying without success to understand what’s happening with Tombstoning, and why my properties which are data bound are not being updated.

To cut a long story short, you can reproduce this using Matt Lacey’s Tombstone Helper Toolkit as follows:

First, download the latest source from CodePlex.

Build for OS 7.1, which requires you to modify the Demo Projects WMAPPManifest.xml file to have the correct AppPlatformVersion 7.1, or you get an error from Microsoft.Phone.PreImport.targets ValidateWMAppManifest rule.

Go to the Debug tab of the demo, and switch on Tombstoning.

Build and run the file, and go to the TextBox demo, and verify that if you change field 1, and navigate away and back, that its value returns.

Now make the following changes:

On line 30 of TextBoxes.xaml, change the line to this, so it’s DataBound as follows:

<TextBox Name="first" Text="{Binding Path=UserName, Mode=TwoWay}"/>


Now, add this to the top of TextBoxes.xaml.cs, inside the namespace declaration.  It’s based on Jesse Liberty's ViewModel here:


public class MainPageViewModel : INotifyPropertyChanged {
private string _userName;
public string UserName
{
get { return _userName; }
set { _userName = value; NotifyPropertyChanged("UserName"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); }
}

public MainPageViewModel()
{
UserName = "I AM SAM";
}
}


Finally, modify Matt’s TextBoxes constructor to set my ViewModel as the DataContext:


public TextBoxes()
{
DataContext = new MainPageViewModel();
InitializeComponent();
}

If you now run the code, and change “I AM SAM” to “SAM I AM”, navigate away, and then back, you’ll see that the name is not replaced.

 

I’ve found a bodge, based on what I read here on the WP7 forums:  Change OnNavigatedTo like this:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Uncomment the next line for changes to be reloaded
// Loaded += (s, ea) =>
{
this.RestoreState();
};
}

 



If you uncomment the Loaded+= line, the code will now work as expected.


Clearly there’s something odd going on with Mango here, as I’ve seen this in my own simplistic implementation where I set the Text property of a TextBox directly without all the framework like this:


Long.Text = (string)State["Longitude"];


and it too does not get updated correctly.

Friday, 23 December 2011

US Culture is more pervasive than you might expect

While getting an application ready to ship, I switched the locale of my WP7 device to Danish.  Fingers crossed, my app will magically display Pi as 3,14159

Alas, that’s not what happens.  When I put up a text box, and enter 3,14159, my software actually gets a value of 314159 instead.

Posting a query on Culture to the WP7 Forums got no result, so I dug into Laurent Bugnion’s excellent Silverlight 4 Unleashed book, and found that he suggests in Listing 6.10 that you set the Language on the Page.  I didn’t really want to build a page for every language, as I’ll never hit them all, so instead modified the constructor of my FrameworkElement classes as follows:

InitializeComponent();
this.Language = System.Windows.Markup.XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentUICulture.Name);


Eventually I tracked down several sources for why this is the case:


http://www.pedrolamas.com/2011/07/28/cuidado-com-o-frameworkelement-language/
http://connect.microsoft.com/VisualStudio/feedback/details/442569/wpf-binding-uses-the-wrong-currentculture-by-default


 


imageimageI also attempted to make it easier for the user to enter numbers into my window, using the InputScope attribute of my TextBoxes, I assumed that InputScope=”Number” would be what I wanted. How Naive.  The image on the left shows how unsuitable this is.


In the Danish Locale, we really want a comma on the keyboard, not a decimal point.  In addition, there is no negative sign.


The best compromise I could find (after randomly trying many of the InputScope values that looked appropriate was Time, which gives the keyboard on the right.


Now I just need to come up with a strategy for translating the Validation Exceptions that I’m currently displaying in the UI.

Wednesday, 21 December 2011

Fast and Loose with WP7 Mutex

While trying to figure out how to synchronize access between my ScheduledAgent and my main program, I started to investigate how Mutex objects behave.

Normally on Windows, when a Mutex is abandoned, an exception is raised when the Mutex is next acquired.  After searching online, I failed to find a description of what happens, particularly if you are holding a Mutex when WP7 decides to kill your process.

Here’s my sample I dropped onto a phone page:

private void OnLoaded(object sender, RoutedEventArgs e)
{
m = new Mutex(false, "KeepOff");
bgw = new Thread(new ThreadStart(bgw_DoWork));
bgw.Start();
try
{
Thread.Sleep(500);
m.WaitOne();
Thread.Sleep(500);
}
catch (Exception ex)
{
}
m.ReleaseMutex();
}

void bgw_DoWork()
{
m.WaitOne();
Thread.Sleep(5000);
Thread.Sleep(0);

}


Surprisingly, when the thread exits, the Mutex is acquired by the code in the try block, and no exception is raised.  I also tested the behavior in my ScheduledAgent, and in that circumstance, the Mutex is also released when the thread is terminated.


Eventually, I did find a reference to the behavior on the phone, but rather than being on WaitOne where I thought it belonged, it’s here: ReleaseMutex Method


Now , I wouldn’t recommend being this sloppy with your Mutex objects, but it looks like for now at least we can assume WP7 will clean up after us.


Peter Torr points out WaitOne might be dangerous, particularly in light of FAS (Fast Application Switching) if it doesn’t include a timeout.  I’ll look into that more in another post.

Thursday, 15 December 2011

ScheduledAgent on WP7 and Asynchronous Events

While working on my LiveTiles, I’ve had a few suggestions that I should wait for an event to occur on loading images.  That’s actually not the problem from what I can tell, but it raised an interesting question.

Let’s say that that I implement my OnInvoke like this:

protected override void OnInvoke(ScheduledTask task)
{
MyEventRaisingObject obj = new MyEventRaisingObject();
obj.SomeAsynchronouslyRaisedEvent += (sender, e) =>
{
// do some work to handle the event, and update our tile
// for example, fetch an xml feed asynchronously
}
NotifyComplete();
}


It looks to me like the OnInvoke will return before my event gets a chance to execute, or in any case I’ll hit my NotifyComplete call.

 

How does one effectively spin until an event has been raised without blocking the thread the object was created on?  Sure I can do this:

 


AutoResetEvent ev1 = new AutoResetEvent(false);
AutoResetEvent ev2 = new AutoResetEvent(false);

...

WaitHandle.WaitAll(new WaitHandle[] { ev1, ev2});

But won’t that block the thread we’re on?

-- update 16 dec --


Don’t even think about using the AutoResetEvent trick above. 


I tried it to wait for my images to be loaded into a BitmapSource, but it seems that the load is done on the UI thread, and as a result, I block the thread waiting for an event to be raised to signal my AutoReset, so that the event can never be raised.

Dynamically generating WP7 Live Tiles

I’ve been struggling to add a generic mechanism for generating live Tiles to my applications.

My requirements are:

  • Save to png format (so I can have a transparent background to always match the users theme)
  • Generate the tile based on a XAML layout.

Saving to PNG

I managed to waste a day creating my own mashup of various source code on line to write PNG’s from a WriteableBitmap.  I started out with some code from Joe Stegman's blog, but hadn’t noticed that I had to fill the EditableImage.  After a day’s rewriting, I managed to build a version that could save an uncompressed png from a WriteableBitmap.  Unfortunately, that produced a LiveTile image of 115KB, which I thought was using too many resources on the phone, just to flip a tile.  And that’s the cost for each side.

I then looked into the .NET ImageTools on CodePlex.  After downloading the source, I couldn’t find some of the source for the referenced DLLs, particularly the gzip libraries.  To make matters worse, it isn’t clear what the license actually is for that component, so after wasting several hours I decided to abandon it, and roll my own compressor from the DotNetZip project on Codeplex.

Finally success after inadvertently using the DeflateStream instead of the GZipStream to write, and now my tiles are down to around 8KB each.

Generating the Bitmap in the first place

As I said above, my second requirement was to generate the bitmap from XAML.

This work in progress class does all of it:

public static class TileExtensions
{
public static readonly string tiledirectory = @"Shared/ShellContent/tiles";
public static IsolatedStorageFile _appstore;

public static IsolatedStorageFile ISO
{
get
{
// lazy open
if (_appstore == null)
{
_appstore = IsolatedStorageFile.GetUserStoreForApplication();
}
return _appstore;
}

}

public static WriteableBitmap GetWriteableBitmapTile(this UIElement item)
{
item.Arrange(new Rect(0, 0, 173, 173));
WriteableBitmap wbmp = new WriteableBitmap(item, null);

return wbmp;
}

public static string GetImageTilePath(string name)
{
return tiledirectory + @"/" + name + ".png";
}

public static Uri GetImageTileUri(string fullpath)
{
return new Uri("isostore:"+fullpath);
}


public static string SaveImageTile(this UIElement tile, string name)
{
WriteableBitmap bmp = tile.GetWriteableBitmapTile();

string fullpath = GetImageTilePath(name);
if (!ISO.DirectoryExists(tiledirectory))
{
ISO.CreateDirectory(tiledirectory); // guarantee our directory exists
}

using (var stream = ISO.OpenFile(fullpath, System.IO.FileMode.OpenOrCreate))
{
bmp.SavePng(stream);
}

return fullpath;
}
}

 

 

 

 


 

The idea then is to load up some XAML as a UserControl, set it’s DataContext, and then render it like this:
var toRender = new MyUserControl;
toRender.DataContext = MyDataContext;
string tilepath = g.SaveImageTile("tilename");
ShellTile primary = ShellTile.ActiveTiles.First();
StandardTileData tile = new StandardTileData()
{
BackBackgroundImage = TileExtensions.GetImageTileUri(tilepath)
};
primary.Update(tile);


This would be a joy to update a tile.  And it all appears to work great.  The databinding works, the control renders.


BUT none of the images from the XAML render.


At the moment, I’ve had to force the loading of the images from my UserControl derived items’ constructor like this:


BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.None;
StreamResourceInfo streamInfo =
Framework.App.GetResourceStream(
new Uri(@"images\img1.png", UriKind.Relative));
bmi.SetSource(streamInfo.Stream);
img1.Source = bmi;


I’ve posted a question on the WP7 Forums, to see what suggestions people might come up with.


A solution:


After much experimentation, and rewriting, the magic came down to subscribing to the LayoutUpdated event of a FrameworkElement.
When the LayoutUpdated event is fired, it appears that the images are guaranteed to be loaded. From that point on you can begin generating the bitmap.
However, there is no way to wait for the event to be fired, so you need to implement some kind of callback.
In the end, my call now looks something like this:

 

TileRenderer tr = new TileRenderer(
new MyUserControl() { DataContext = new MyTileInfo() });
tr.SaveImageTile("tilefilename",
(x) => {
ShellTile primary = ShellTile.ActiveTiles.First();
StandardTileData tile = new StandardTileData()
{
BackBackgroundImage = x.GetImageTileUri()
};
primary.Update(tile);
});