Thursday, 28 May 2015

Windows Live Writer no longer works with Blogger

I'm not a frequent blogger, so have only been bitten by this issue today - but checking the internet for clues it seems I'm not the only one. I wrote a long blog post in Live Writer and then tried to upload it, only to see this error message come up...

Being a geek I looked into this further and found that it was an issue with how Windows Live Writer authenticates (i.e. logs in) to Blogger. Google have disabled the mechanism that WLW was using, and so at this present moment (28th May 2015) you can't use WLW with a Blogger account. Pants.

There is currently no workaround (for WLW that is) - you simply cannot use it.

If you're on the Blogger platform you can use their tools to blog with (that's what I'm using here), or try out some other blog tools on the web. For now I'll limp along with the Blogger UI, it's OK - but nowhere near as easy to use as Windows Live Writer. Sadly it seems that Microsoft are no longer maintaining WLW, so you may be waiting a long while for a fix.

Adventures in Application Insights - Part 1

If, like I was, you’re struggling to get anything out of Azure Application Insights, I hope to be of some help.

It all started well enough – I added App Insights to a web app, this setup the ApplicationInsights.config file for me and boom, I had some data in App Insights on the web. Then I wanted to write a custom event to the cloud so I added the following code into the app…

  var tc = new TelemetryClient();
  tc.TrackEvent("Testing");
  tc.Flush();

I ran my app and imagine my dismay when nothing happened. I checked around in blog posts, looked at the official documentation, tried a bunch of things – but stubbornly the above lines of code simply didn’t want to work for me.

Instrumenting a Console App

The web project I’m adding this to isn’t the simplest, so rather than hack it around too much I then decided to create a simple Console App and debug through it to see what was happening. Here it is in its entirety…

using Microsoft.ApplicationInsights;
namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            var tc = new TelemetryClient();
            tc.TrackEvent("Testing");
            tc.Flush();
        }
    }
}

Not a great deal going on there! Having seen the ApplicationInsights.config file in the other project I added one to my console app too – well, I copied it from the web app to be fair. I ran the app and once again – nothing. At that point I decided it was time to do some debugging – but I couldn’t find symbols for Microsoft.ApplicationInsights.dll (and others from the App Insights stable), so I dragged out my trusty copy of Reflector and created some. If you didn’t already know, Reflector can generate missing PDB’s for you, it’s cooler than a penguins chilly bits and well worth the cost.

With a .pdb in hand I then stepped into the TrackEvent method and further down the call chain until I got to this point in the code…

[EditorBrowsable(EditorBrowsableState.Never)]
public void Track(ITelemetry telemetry)
{
  if (this.IsEnabled()
  {
    string instrumentationKey = this.Context.InstrumentationKey;
    if (string.IsNullOrEmpty(instrumentationKey))
    {
      instrumentationKey = this.configuration.InstrumentationKey;
    }
    if (!string.IsNullOrEmpty(instrumentationKey))
  ...

When I inspected the instrumentationKey it was empty - and this indicated to me that despite having an ApplicationInsights.config file, this wasn't actually being picked up. A quick look on disk and I saw that the file wasn't in the same directory as my .exe, so I went back to the app and set the properties of the file as shown below...

The main thing to note is that I changed Copy To Output Directory. With that altered I ran the app again and this time Boom, it threw an exception…

Drilling down on the exception the root cause was this…

{"Type 'Microsoft.ApplicationInsights.Extensibility.RuntimeTelemetry.RemoteDependencyModule, Microsoft.ApplicationInsights.Extensibility.RuntimeTelemetry' could not be loaded."}

The Config File

This alerted me to the content of the .config file that I'd blatantly copied and pasted from the web project, and opening it up it was evident what was happening - I'd 'told' it to load some types that didn't exist in my simple console app - or rather, the file I copied across included a bunch of types that were not referenced...

<?xml version="1.0" encoding="utf-8"?>
<?XML:NAMESPACE PREFIX = "[default] http://schemas.microsoft.com/ApplicationInsights/2013/Settings" NS = "http://schemas.microsoft.com/ApplicationInsights/2013/Settings" /><applicationinsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
  <!-- 
    Learn more about Application Insights configuration with ApplicationInsights.config here: 
    http://go.microsoft.com/fwlink/?LinkID=513840
    
    Note: If not present, please add <InstrumentationKey>Your Key</InstrumentationKey> to the top of this file.
  -->
  <telemetrymodules>
    <add type="Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.DiagnosticsTelemetryModule, Microsoft.ApplicationInsights"></add>
    <add type="Microsoft.ApplicationInsights.Extensibility.RuntimeTelemetry.RemoteDependencyModule, Microsoft.ApplicationInsights.Extensibility.RuntimeTelemetry"></add>
    <add type="Microsoft.ApplicationInsights.Extensibility.PerfCollector.PerformanceCollectorModule, Microsoft.ApplicationInsights.Extensibility.PerfCollector">
...

Here the TelemetryModules section is quite clearly defining a bunch of plug-ins, the first exists (as it's in Microsoft.ApplicationInsights which I have referenced), the second and third do not and sure enough the second is the subject of the exception I received. Armed with this knowledge I did a hatchet job on the .config file and removed anything I didn't have referenced from Nuget or directly. I then ran the app again and to my dismay - still nothing happened. I was expecting to see an HTTP Post request flying over the wire to Azure, but instead there was nothing.

Flush, aha, he saved every one of us!

(Sorry for the abysmal pun, I couldn’t help myself). I did a bit more digging on the TelemetryClient and found the Flush() method which I have added into the code samples above, but didn't have in my code at the time. With this added I was delighted to see a message show up inside Visual Studio that alerted me to the fact that I had managed to send an event to Azure..

I also saw this pop up in Fiddler...

So far so good. I then logged into Azure and lo and behold, I got my event!

I could even drill down and see “more” data about the event…

OK, so “more” data is somewhat subjective, but it did tell me that my event came from the UK which is correct. I have no doubt that I could augment the data being collected from my application so that the amount of detail here would be better.

Looking at what was sent to the server, it’s a simple JSON request as follows (I’ve blanked out my App Insights key, which I’m surprised to find twice in the content. If I’d designed this, the app insights key would have been a custom header, but that’s just an aside)…

And there you have it – one event in Azure, an hour or more messing around with it to understand what it’s doing, and I’ve got a load closer to working out why my web app wasn’t sending any telemetry (or rather, the custom telemetry I was expecting to send).

I hope that helps someone else! Next time I’ll see what’s up with my web app.

Sunday, 10 May 2015

Process Monitor saves the day

For some time now on my main development machine I’ve been having issues – Visual Studio has begun taking an extremely long time to load, and when running or debugging web apps these too have been taking too long to startup.

It’s one of those issues where you put off fixing it for a while as it’s not too bad, but then it gets to the point where it’s really hampering the dev/test/debug cycle, so this morning I set about working out what it could be. I disabled the extensions I have loaded and that made little difference, so then went to ProcessMonitor to see if I could work out what was happening.

In the case of Visual Studio, it would take around a minute to start a fresh copy. This was way too long. For websites, it was maybe 30 seconds or so – again, way too long. With process monitor running I created a new instance of Visual Studio, waited until it was done, and then paused event capture and went to have a look into the events.

It wasn’t long before I noticed a *lot* of files being written to the C:\logs directory – on my machine I keep this for one of two file types, actual log files from my code, and log files from the assembly log viewer, FusLogVw.exe. Smoking gun in hand I had a quick look at the size of the Logs directory and it was up to 8GB! Oops.

A small while later (after disabling FusLogVw.exe and deleting the files), I’m back to snappy performance with VS loading in about 4 seconds, and websites in a second or so.

Normal service resumed. Phew!

Wednesday, 28 January 2015

Xamarin Forms contacts search

I’m in the middle of writing a Xamarin Forms app and today I needed to add in a contacts search page, and remembered that James Montemagno had created a plugin that exposes contacts in a platform neutral manner so I downloaded it and used it in my app.

I also wanted to add a search bar, and again there’s a control in XF for that, so I ended up with (somewhat simplified) the following XAML…

  
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<SearchBar x:Name="search" Placeholder="Search">
</SearchBar>
<ListView Grid.Row="1" ItemsSource="{Binding FilteredContacts}"
IsGroupingEnabled="true" GroupDisplayBinding="{Binding Key}"
GroupShortNameBinding="{Binding Key}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" TextColor="Black"
Detail="{Binding PhoneNumber}" DetailColor="Gray">
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

I have removed some of the XAML as it’s not that important to this post (it’s available in the download). Now, with that in place (and a load of code in the view model which I’ll get to in a minute) I got a UI as follows…


iOS Simulator Screen Shot 28 Jan 2015 22.32.19


So far so good. Then I needed to hook up the search box, and as I’m using XAML (and if you’re not, you should give it a try as it’s way easier to create UI’s using it) I needed a way to bind to the search box to that I could respond to the TextChanged event.


Another excellent package that you’ll want to use it the Xamarin.Behaviors package by Corrado – massive thanks to him for putting this together, it’s excellent!


Behaviors to the rescue


By adding a behavior into the XAML, I can handle an event – so in this case I added the following to the SearchBar…

  <SearchBar x:Name="search" Placeholder="Search">
<b:Interaction.Behaviors>
<b:BehaviorCollection>
<b:EventToCommand EventName="TextChanged"
Command="{Binding SearchTextChanged}"
CommandParameter="{Binding Text, Source={x:Reference search}}"/>
</b:BehaviorCollection>
</b:Interaction.Behaviors>
</SearchBar>

This hooks the TextChanged event of the search bar, calls the SearchTextChangedCommand on my view model, and passes through the value of the Text property of the search bar. Yay!.


Or not.


The problem I found was that my command was being passed a string, but it was the text before the ne character was entered, so say I pressed ‘X’ in an empty search bar, my code would be called with an empty string. Pressing ‘A’ next, my command would get ‘X’, and pressing ‘M” next, my code would get ‘XA’. I was always getting the data just prior to the new character – so I guess that the TextChanged event should be more clearly termed as the TextChanging event.


Anyway, I needed to fix this – so looked at the actual event and it contains two properties, the current text and the new value. All I needed to do was get the new vale of the event arguments and I’d be away.


To the best of my knowledge there is no way to bind to the event arguments, you need to write some additional code (this was true in WPF and Silverlight, I’ve done exactly the same there too). So, I cranked out a new behavior that attaches to the SearchBar’s TextChanged event, and calls the command with the new value of the text property. My XAML is therefore this…

  <SearchBar x:Name="search" Placeholder="Search">
<b:Interaction.Behaviors>
<b:BehaviorCollection>
<bh:SearchBarTextChanged Command="{Binding SearchTextChanged}"/>
</b:BehaviorCollection>
</b:Interaction.Behaviors>
</SearchBar>

Here I'm using my SearchBarTextChanged behavior to hook to the SearchTextChanged command, and sure enough now when I type in the search bar I get the desired effect. Excellent!


Filtering in code


In the view model I use James’ contacts plugin to select all contacts that have a mobile phone, and that have at least a forename or surname (my real code blew up on a colleagues iPhone as he has a contact with just a company name). I tuck this list away as the source, and then create a filtered collection from the source and the filter statement.

CrossContacts.Current.PreferContactAggregation = false;
var hasPermission = CrossContacts.Current.RequestPermission().Result;

if (hasPermission)
{
// First off convert all contacts into ContactViewModels...
var vms = CrossContacts.Current.Contacts.Where(c => Matches(c))
.Select(c => new ContactViewModel(c));

// And then setup the contact list
var grouped = from contact in vms
orderby contact.Surname
group contact by contact.SortByCharacter into contactGroup
select new Grouping (contactGroup.Key, contactGroup);

foreach (var g in grouped)
{
_contacts.Add (g);
_filteredContacts.Add (g);
}
}

The above code uses some Linq to select all contacts and group them by first character of their surname. I created a simple Matches(contact) function that checks that the contact has a mobile phone number and also that they have one of forename or surname.


Then I have the code that is called to filter the collection when you type text into the search bar...

private void FilterContacts(string filter)
{
_filteredContacts.Clear ();

if (string.IsNullOrEmpty(filter))
{
foreach (var g in this.Contacts)
_filteredContacts.Add (g);
}
else
{
// Need to do some filtering
foreach (var g in this.Contacts)
{
var matches = g.Where (vm => vm.Name.Contains (filter));

if (matches.Any())
{
_filteredContacts.Add (new Grouping (g.Key, matches));
}
}
}
}

This is a bit ropey but does the trick. As the collection is an ObservableCollection, any changes are shown in the UI immediately.


Code


I’ve created a simple example project (the one shown above) that you can download. Hopefully this will help someone out. I’ve not tried this on Android or Windows Phone as yet, but as none of the code is in the platform specific library I can’t see any reason for it not to work on those platforms too.


Bye for now!

Friday, 23 January 2015

Xamarin Forms Navigation–Dealing with Login

This is the third in a series of posts on Xamarin Forms – in this instalment I want to add login capability to my app, but this isn’t as simple as I thought it would be. I’m used to crafting interfaces with XAML in WPF and Silverlight, and have been doing so for several years now, but with Xamarin Forms things are a little different.

Here’s what I want to be able to do, courtesy of Visio…

image

When the app starts up I’ll check if the user has a valid token, and if so skip the login part and go straight to the main page.

If however the user hasn’t yet logged in I want to show a “carousel” where I can do some app advertising, then let them choose from one of the login providers I’ve integrated, and once logged in show the main form. The gnarly part here is the navigation support – once at the MainForm stage I don’t want to allow the user to “back out” into the login part, and so far on the intertubes I’ve not seen a clear way of doing this so looked into this myself.

I’m using Xamarin Forms 1.3 which has improved navigation support, so the first thing I tried was a main form that cleared out the navigation stack when it showed up. This worked to a fashion, in that the back-stack was cleared, but it left a “< Back” button on the navigation section when I arrived at the main form, and that was less than ideal.

After trying some other options (non of which panned out) I went back to first principles and looked at how the application startup sequence worked, and from that arrived at a simple solution – at the point where I need to display the main page, I simply call the following…

    var nav = new NavigationPage(new MainPage());
nav.BarBackgroundColor = Color.FromHex("#195174");
nav.BarTextColor = Color.White;

Application.Current.MainPage = nav;

And boom - the main page is shown and there's no previous pages that can be navigated to.

I’d originally created my main page and set that as the root, then coded the login forms as modal, and whilst that worked OK it had the undesired behaviour of showing the main form first (briefly), then navigating to the login page. That wasn’t particularly great as I want login to be the first thing seen when the application is run for the first time.

The downside of my approach here is that there’s no transition between the last login page and the main form, I haven’t found a way to do that at the moment.

I’m not sure if this is the best method either – however there doesn’t seem to be (or I cannot find) any alternatives that work quite how I want them to. I want to be able to use navigation during login, as I have a carousel page and then a provider selection page (and maybe more pages after that, once I’ve written them!), so using a Modal login page doesn’t suit my purposes.


Demo Code


If you would like to grab my example project please download it here. The structure is deliberately simple (I usually use Autofac and XAML in my projects, this has neither as there’s less to setup). When you run the app you’re on the carousel view which consists of three pages…


IMG_1304    IMG_1305    IMG_1306


I’m using a carousel here and a timer to move between the images, you can swipe too should you wish to. When you click on the “Providers” button a new page is shown, where you would ask the user to choose their login provider. I’ve cheated and just shown a login button…


IMG_1307


When the Login button is clicked I construct a new navigation page and the main page within it, and then set the application root page to this combination using the code shown above which gives the main page shown below.


IMG_1308




You can navigate elsewhere using the button…


IMG_1309  IMG_1310


And you can navigate directly back to the root page when you’re on the “At the end” page.


Conclusion


In this example I’ve attempted to show how you can “break out” of the navigation hierarchy and start a new navigation stack. There may be a better way to do this and if so please let me know!

Tuesday, 20 January 2015

Self hosting workflow–working with InstanceStore and InstanceOwner

I’ve not been doing a great deal of Workflow recently but had to fix an issue at a customer today and as there’s such poor information about this on the Web I put fingers to keys to blog about it.

If you are self-hosting workflows using WorkflowApplication you’ll typically have some code such as the following to setup the workflow instance store…

    instanceStore = new SqlWorkflowInstanceStore
(@"Data Source=.\SQLEXPRESS;Initial Catalog=SampleInstanceStore;
Integrated Security=True;Asynchronous Processing=True");

InstanceHandle handle = instanceStore.CreateInstanceHandle();
InstanceView view = instanceStore.Execute(handle,
new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
handle.Free();

instanceStore.DefaultInstanceOwner = view.InstanceOwner;

The question is - where does this go? I’ll get on to that in a moment.


When you want to run a workflow you need to set it’s instance store – so you’ll typically do this…

    WorkflowApplication application = new WorkflowApplication(activity);
application.InstanceStore = instanceStore;

And then somewhere online you might have seen the DeleteWorkflowOwnerCommand as that’s the opposite of the CreateWorkflowOwnerCommand so has to be called somewhere, doesn’t it?


If all of this has you confused this is the article that will help you.


Instance Store: One Per Process


When creating the instance store you need no more than one per process. You can run multiple workflow instances in this process, and each will play nicely together – but all will use the same store. You don’t need loads, one will do per process.


I’m using AutoFac so have setup a dependency to be SingleInstance(), and each workflow wrapper that I run imports that single instance. The wrapper is shown below…

    public interface IWorkflowInstanceStore
{
InstanceStore Store { get; }
}

The implementation is also pretty trivial...

    public class WorkflowInstanceStore : IWorkflowInstanceStore, IDisposable
{
public WorkflowInstanceStore(string connectionString)
{
_instanceStore = new SqlWorkflowInstanceStore(connectionString);

InstanceHandle handle = _instanceStore.CreateInstanceHandle();
InstanceView view = _instanceStore.Execute(handle,
new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
handle.Free();

_instanceStore.DefaultInstanceOwner = view.InstanceOwner;
}

public InstanceStore Store
{
get { return _instanceStore; }
}

public void Dispose()
{
if (null != _instanceStore)
{
var deleteOwner = new DeleteWorkflowOwnerCommand();
InstanceHandle handle = _instanceStore.CreateInstanceHandle();
_instanceStore.Execute(handle, deleteOwner, TimeSpan.FromSeconds(10));
handle.Free();
}
}

private InstanceStore _instanceStore;
}

As you'll see here this is a disposable class and in the Dispose() method I call the DeleteWorkflowOwnerCommand. When running workflows, you'll get a row in the LockOwners for each instance store, and these are tidied up by the RecoverInstanceLocks stored procedure which runs periodically. This can be a source of poor performance, as if you use an instance store per workflow application (which is very common given the lack of guidance on this matter), then you'll end up with a large number of rows in this table and RecoverInstanceLocks will show up on your SQL traces as taking a long time to execute. The culprit isn't directly the stored procedure, it's the use of too many rows in the LockOwnersTable. Typically there should be just one row in there per process that runs workflows.


Workflow Runner: One per workflow application


Next up you’ll need a class that runs workflows – typically you’ll write a simple wrapper such as the one I’ve done below, this wraps running workflows and resuming bookmarks with Task based operations so that you can easily integrate Workflow into your code. Note – you might want to process other events and probably do some logging inside this class too, I’ve just provided a minimal implementation…

    public class WorkflowHoster
{
public WorkflowHoster(IWorkflowInstanceStore instanceStore,
params object[] services)
{
_instanceStore = instanceStore.Store;
_services = services.ToList();
}

public Task RunInstance(Activity root)
{
WorkflowApplication application = new WorkflowApplication(root);
foreach (var service in _services)
application.Extensions.Add(service);
var tcs = new TaskCompletionSource();

application.InstanceStore = _instanceStore;

application.PersistableIdle = (e) => PersistableIdleAction.Unload;
application.Unloaded = (e) => tcs.SetResult(e.InstanceId);
application.Aborted = (e) => tcs.SetException(e.Reason);
application.Run();

return tcs.Task;
}

public Task ResumeInstance(Activity root, Guid workflowInstanceId,
string bookmarkName, object bookmarkData = null)
{
WorkflowApplication application = new WorkflowApplication(root);
application.InstanceStore = _instanceStore;
var tcs = new TaskCompletionSource();

application.PersistableIdle = (e) => PersistableIdleAction.Unload;
application.Unloaded = (e) => tcs.SetResult(DateTime.UtcNow);
application.Aborted = (e) => tcs.SetException(e.Reason);
application.Load(workflowInstanceId);
application.ResumeBookmark(bookmarkName, bookmarkData);

return tcs.Task;
}

private InstanceStore _instanceStore;
private AutoResetEvent _instanceUnloaded = new AutoResetEvent(false);
private List<object> _services;
}

You may also want to pass parameters to the workflow instance etc. - as I mentioned the above is a minimal implementation just to show how this works.


Running a workflow


With that stuff defined you can run a workflow as follows…

    var store = GetThePerProcessStoreFromSomewhere();

var host = new WorkflowHoster(store);
Task wf = host.RunInstance(rootActivity);
// Now wait for the wf task to complete...

The task will complete when the workflow unloads (which obviously happens if the workflow completes entirely, or if it goes idle and persists due to a bookmark or delay).


Wrap Up


If you want to self-host workflow, then using your own persistence store requires a bit of knowledge which is hard to come by on the intertubes.


Create a single instance-store per process, and only call DeleteWorkflowOwnerCommand once when your process is shutting down. That should be all you need.

Sunday, 18 January 2015

Xamarin Forms–Using background images on iOS

Following on from my last post on creating A Light status bar for iOS, I wanted to go one step further and add in a full-screen background image to my application. This is very easy without a navigation bar, but with one it’s a bit more involved so I thought a blog post would be in order.

If you have no navigation bar then all you really need to do is create an image of the right size (for each physical phone you want to support, such as 4S, 5S, 6, 6 Plus), add images to the resources directory and use them in code or XAML. With a navigation bar it’s a bit more involved as you need to split your source image into two separate images (one for the navigation bar and one for the remainder of the screen).

Then when you have the images defined you need to hook the images to the correct controls, and this is where it gets a little more complex. With a NavigationPage we need to set the navigation bar’s background to an image – but we can’t do this in regular Xamarin Forms code as the property is only available in a custom renderer, so we’ll need to create one.

First off though we need an image – I chose one I took in 2006 whilst on holiday with my family – this is the last time we were all together and it was a beautiful day. Anyhow, I extracted four images in total as shown in the following…

Original Marked Up

The red outline contains the navigation bar image (640px * 128px) and the background image (640px * 1008px) – these are both for an iPhone 5S which is what I have to develop on at the moment.

The green outline contains the two images for the landscape view, the navigation bar being 1136px * 64px, and the background image being 1136px * 576px. The navigation bar is shorter by default in landscape mode as the status bar is switched off. I believe it’s possible to override this and display the status bar, but I don’t need that so haven’t looked into it further.

I named those images as follows…

  • PortraitNavBar@2x.png
  • PortraitBackgrouns@2x.png
  • LandscapeNavBar@2x.png
  • LandscapeBackground@2x.png

With that done I then needed to write a bit of code. First up I had to ensure that the correct background image was shown for the content page – this was simply a job of overriding the OnSizeAllocated member of my content page…

    protected override void OnSizeAllocated (double width, double height)
{
base.OnSizeAllocated (width, height);

if (width < height)
this.BackgroundImage = "PortraitBackground.png";
else
this.BackgroundImage = "LandscapeBackground.png";

}

The OnSizeAllocated method is called when the size changes on the phone and it provides the size of the page, so this seemed like a reasonable place to determine which image to load up.


Then I needed to add in a custom render so added a deriver navigation page as shown below…

    public class CustomNavigationPage : NavigationPage
{
public CustomNavigationPage (Page content) : base(content)
{
}
}

With that defined I could then add the renderer. The purpose of this renderer is to render the background image of the navigation bar...

[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(XamFormsTestbed.iOS.Renderers.CustomNavigationPageRenderer))]

namespace XamFormsTestbed.iOS.Renderers
{
public class CustomNavigationPageRenderer : NavigationRenderer
{
public override void ViewDidLoad ()
{
base.ViewDidLoad ();

var img = UIImage.FromBundle ("PortraitNavBar.png");
this.NavigationBar.SetBackgroundImage (img, UIBarMetrics.Default);

img = UIImage.FromBundle ("LandscapeNavBar.png");
this.NavigationBar.SetBackgroundImage (img, UIBarMetrics.Compact);
}
}
}

Originally I'd tried responding to size changes here, but then read an article that discussed the above mechanism and it appeared to work.


With all that in place I can now display a background image in the navigation bar and also have a correctly formatted image for both landscape and portrait orientations...

IMG_1302

IMG_1303


And there you have it – a full background image with a navigation bar. Hopefully this will help someone with their application.