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

Thursday 8 December 2011

Why isn’t my TaskDialog PInvoke call working?

I’m trying to track down a problem today where one of our programs apparently locks up.

Here’s the screengrab:

what

Notice the partially drawn window around the center, with leftover high scores from the previous window.  I’m pretty sure this outer box is the window frame for the task dialog, as that’s where it’s positioned when it is shown correctly.  The high scores dialog box that was on the screen was larger, and the buttons and bottom of the screen have redrawn.

Here’s how I declare the signature:

[DllImport("comctl32.dll", PreserveSig = false, CharSet = CharSet.Unicode)]
private static extern TaskDialogResult TaskDialog(IntPtr hwndParent, IntPtr hInstance,
string title, string mainInstruction, string content,
TaskDialogButtons buttons, TaskDialogIcon icon);



And here’s how I call it:


int split = text.IndexOf('\n');

TaskDialogResult tdr = TaskDialog(hWnd, IntPtr.Zero, caption,
text.Substring(0, split),
text.Substring(split + 1), taskBtn, taskIcon);



Now, what I think is probably happening is that for some reason, the two strings passed into the TaskDialog are being garbage collected, but that’s just a wild guess at the moment.

Wednesday 7 December 2011

Lost Exceptions inside DispatcherTimer Tick Handler

I have been using LittleWatson in my WP7 apps to catch unhandled exceptions and report them back to me by email if the user so chooses.

However, I found that I had a stack trace on Marketplace but no report.  I thought it might be because the user hadn’t sent the message, so instead forced the exception I was guessing I had an OutOfMemory exception, as the stack trace had a LoadJpeg call in it for a 1600x800 image.

So I added this line:

throw new OutOfMemoryException()

To my surprise, LittleWatson didn’t catch it.  So I threw the same exception at various other places in my code, and LittleWatson did catch it.

What I discovered (In 7.10.7720.68 at least) is that OutOfMemoryExceptions are swallowed, and appear to terminate your application inside a DispatcherTimer Tick Handler.