MvvmCross - navigation with custom objects - xamarin

I have followed the steps in this link
Passing complex navigation parameters with MvvmCross ShowViewModel
i implemented an instance of the IMvxJsonConverter, and registered it. this is my code for my view model
public class AccountDetailsViewModel : BaseViewModel<AccountDetailsNav>
{
private readonly Repository.AccountsRepository _accounts;
Account _fullAccount;
public AccountDetailsViewModel(Repository.AccountsRepository accounts)
{
_accounts = accounts;
}
protected override void RealInit(AccountDetailsNav parameter)
{
//stuff
}
I have tried simple types by just passing thru strings , this is the code i use to navigate to to the viewmodel
Mvx.RegisterSingleton<Repository.AccountsRepository>(() =>
{
return _accounts;
});
ShowViewModel<AccountDetailsViewModel>(nav);
But it never ever seems to arrive in my view model methods or populates my data, and i cannot for the life of me figure out why. the data is serialized fine , and i have even tried blank constructors to no avail .. i just cannot figure out why its not hitting the realinit

K i found the problem , when adding a new view i failed to remove this method on the code behind of the view, and as such was causing my viewmodel to be null and never hitting my breakpoints
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}

Related

How to use MvvmCross without storyboards in iOS?

When I was using MvvmCross 5, I coded all my views and avoided using storyboards for my iOS app by writing this in my AppDelegate:
[Register("AppDelegate")]
public class AppDelegate : MvxApplicationDelegate
{
private MvxIosViewPresenter viewPresenter;
public override UIWindow Window
{
get;
set;
}
/// <summary>
/// MvvmCross Mods:
/// - Creates a new presenter, which determines how Views are shown
/// - This example uses a standard presenter.
/// </summary>
/// <param name="application"></param>
/// <param name="launchOptions"></param>
/// <returns></returns>
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// MvvmCross Mod Start---------------------------------------------
// This class will determine how Views are shown
this.viewPresenter = new MvxIosViewPresenter(this, Window);//new ViewPresenter(Window);
// Init the Setup object, which initializes App.cs
var setup = new Setup(this, this.viewPresenter);
setup.Initialize();
//this.viewPresenter.PresentModalViewController(new ListenViewController(), true);
// Use IoC to find and start the IMvxAppStart object
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
// MvvmCross Mod End--------------------------------------------------
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
public class Setup : MvxIosSetup
{
public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter)
: base(appDelegate, presenter)
{
}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override IMvxTrace CreateDebugTrace()
{
return new DebugTrace();
}
}
But in MvvmCross 6.4.2, MvxIosSetup does not have the base constructor that takes 2 arguments. Instead I have:
[Register(nameof(AppDelegate))]
public partial class AppDelegate : MvxApplicationDelegate<Setup, App>
{
}
public class Setup : MvxIosSetup<App>
{
}
How can I configure it so that I can code my views without storyboards?
EDIT
I created a very small sample app with 1 view model/controller, using MvvmCross 7. The ViewDidLoad method never gets called in my MainViewController. Can someone please tell me why? I put my code here:
https://github.com/sinight95/TestApp/tree/main/TestApp
Neither the Setup.cs nor AppDelegate.cs files have anything to do whether you are presenting a storyboard or not. It is usually all up to which Presentation Attributes you are applying to a ViewController.
However, there are some stuff set in the Info.plist that changes how MvvmCross expects things to be set up.
Now in your example App you've put up on GitHub, you can do the following to make it not use the storyboard:
Remove Main.storyboard
In Info.plist set Main interface to (none)
In Info.plist set launch images to LaunchScreen.storyboard
Remove the scene delegate stuff in Info.plist
Remove the constructor in MainViewController
What is the main issue here is that you are essentially telling iOS that the ViewController has a storyboard by supplying this constuctor:
public MainViewController() : base(nameof(MainViewController), null)
{
}
Also are telling iOS to use storyboards through the Info.plist with all the scene delegation stuff.
I've created a PR showing what needs to be changed to make it run without the storboard and show the blue background color you've set on your ViewController.
https://github.com/sinight95/TestApp/pull/1

OnAppearing different on iOS and Android

I have found that on iOS, OnAppearing is called when the page literally appears on the screen, whereas on Android, it's called when it's created.
I'm using this event to lazily construct an expensive to construct view but obviously the Android behaviour defeats this.
Is there some way of knowing on Android when a screen literally appears on the screen?
You can use the event:
this.Appearing += YourPageAppearing;
Otherwise, you should use the methods of the Application class that contains the lifecycle methods:
protected override void OnStart()
{
Debug.WriteLine ("OnStart");
}
protected override void OnSleep()
{
Debug.WriteLine ("OnSleep");
}
protected override void OnResume()
{
Debug.WriteLine ("OnResume");
}
On Android, Xamarin.Forms.Page.OnAppearing is called immediately before the page's view is shown to user (not when the page is "created" (constructed)).
If you want an initial view to appear quickly, by omitting an expensive sub-view, use a binding to make that view's IsVisible initially be "false". This will keep it out of the visual tree, avoiding most of the cost of building it. Place the (invisible) view in a grid cell, whose dimensions are constant (either in DPs or "*" - anything other than "Auto".) So that layout will be "ready" for that view, when you make it visible.
APPROACH 1:
Now you just need a binding in view model that will change IsVisible to "true".
The simplest hack is to, in OnAppearing, fire an action that will change that variable after 250 ms.
APPROACH 2:
The clean alternative is to create a custom page renderer, and override "draw".
Have draw, after calling base.draw, check an action property on your page.
If not null, invoke that action, then clear it (so only happens once).
I do this by inheriting from a custom page base class:
XAML for each of my pages (change "ContentPage" to "exodus:ExBasePage"):
<exodus:ExBasePage
xmlns:exodus="clr-namespace:Exodus;assembly=Exodus"
x:Class="YourNamespace.YourPage">
...
</exodus:ExBasePage>
xaml.cs:
using Exodus;
// After creating page, change "ContentPage" to "ExBasePage".
public partial class YourPage : ExBasePage
{
...
my custom ContentPage. NOTE: Includes code not needed for this, related to iOS Safe Area and Android hardward back button:
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Exodus
{
public abstract partial class ExBasePage : ContentPage
{
public ExBasePage()
{
// Each sub-class calls InitializeComponent(); not needed here.
ExBasePage.SetupForLightStatusBar( this );
}
// Avoids overlapping iOS status bar at top, and sets a dark background color.
public static void SetupForLightStatusBar( ContentPage page )
{
page.On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea( true );
// iOS NOTE: Each ContentPage must set its BackgroundColor to black or other dark color (when using LightContent for status bar).
//page.BackgroundColor = Color.Black;
page.BackgroundColor = Color.FromRgb( 0.3, 0.3, 0.3 );
}
// Per-platform ExBasePageRenderer uses these.
public System.Action NextDrawAction;
/// <summary>
/// Override to do something else (or to do nothing, i.e. suppress back button).
/// </summary>
public virtual void OnHardwareBackButton()
{
// Normal content page; do normal back button behavior.
global::Exodus.Services.NavigatePopAsync();
}
}
}
renderer in Android project:
using System;
using Android.Content;
using Android.Views;
using Android.Graphics;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Exodus;
using Exodus.Android;
[assembly: ExportRenderer( typeof( ExBasePage ), typeof( ExBasePageRenderer ) )]
namespace Exodus.Android
{
public class ExBasePageRenderer : PageRenderer
{
public ExBasePageRenderer( Context context ) : base( context )
{
}
protected override void OnElementChanged( ElementChangedEventArgs<Page> e )
{
base.OnElementChanged( e );
var page = Element as ExBasePage;
if (page != null)
page.firstDraw = true;
}
public override void Draw( Canvas canvas )
{
try
{
base.Draw( canvas );
var page = Element as ExBasePage;
if (page?.NextDrawAction != null)
{
page.NextDrawAction();
page.NextDrawAction = null;
}
}
catch (Exception ex)
{
// TBD: Got Disposed exception on Android Bitmap, after rotating phone (in simulator).
// TODO: Log exception.
Console.WriteLine( "ExBasePageRenderer.Draw exception: " + ex.ToString() );
}
}
}
}
To do some action after the first time the page is drawn:
public partial class YourPage : ExBasePage
{
protected override void OnAppearing()
{
// TODO: OnPlatform code - I don't have it handy.
// On iOS, we call immediately "DeferredOnAppearing();"
// On Android, we set this field, and it is done in custom renderer.
NextDrawAction = DeferredOnAppearing;
}
void DeferredOnAppearing()
{
// Whatever you want to happen after page is drawn first time:
// ((MyViewModel)BindingContext).ExpensiveViewVisible = true;
// Where MyViewModel contains:
// public bool ExpensiveViewVisible { get; set; }
// And your XAML contains:
// <ExpensiveView IsVisible={Binding ExpensiveViewVisible}" ... />
}
}
NOTE: I do this differently on iOS, because Xamarin Forms on iOS (incorrectly - not to spec) calls OnAppearing AFTER the page is drawn.
So I have OnPlatform logic. On iOS, OnAppearing immediately calls DeferredOnAppearing. On Android, the line shown is done.
Hopefully iOS will eventually be fixed to call OnAppearing BEFORE,
for consistency between the two platforms.
If so, I would then add a similar renderer for iOS.
(The current iOS implementation means there is no way to update a view before it appears a SECOND time, due to popping the nav stack.
instead, it appears with outdated content, THEN you get a chance
to correct it. This is not good.)

Uninitialised JsonSerializer in Breeze SaveBundleToSaveMap sample

I'm attempting to use the SaveBundleToSaveMap snippet linked below to implement custom save handling on the server side of a breeze web api implementation.
SaveBundleToSaveMap
This sample does not work as is? (see below); their is a null reference exception which could use some attention.
The SaveWorkState(provider, entitiesArray) constructor calls the ContextProvider.CreateEntityInfoFromJson(...) method which then calls (the class scoped) JsonSerializer.Deserialize(new JTokenReader(jo), entityType) method.
The issue is that JsonSerializer is uninitialised and we get a null reference exeption.
For e.g. I added this test hack to get the code running:
protected internal EntityInfo CreateEntityInfoFromJson(dynamic jo, Type entityType) {
//temp fix to init JsonSerializer if SaveChanges has NOT been called
if(JsonSerializer==null) JsonSerializer = CreateJsonSerializer();
var entityInfo = CreateEntityInfo();
entityInfo.Entity = JsonSerializer.Deserialize(new JTokenReader(jo), entityType);
entityInfo.EntityState = (EntityState)Enum.Parse(typeof(EntityState), (String)jo.entityAspect.entityState);
entityInfo.ContextProvider = this;
This issue does not occur in the standard release bits as CreateEntityInfoFromJson is always? called downstream from a SaveChanges() call which means the JsonSerializer gets initialised.
However, things would be better structured if an initialised JsonSerializer was passed to CreateEntityInfoFromJson as a parameter to avoid potential future null reference issues?
Alternately, is there a way to get the SaveBundleToSaveMap snippet to init the JsonSerializer? Its got a private setter :(
UPDATE
Implemented a very hacky stopgap solution. If anyone at IdeaBlade is watching, would be great to have a public API to convert to and from json saveBundle <-> saveMap.
/// <summary>
/// Convert a json saveBundle into a breeze SaveMap
/// </summary>`enter code here`
public static Dictionary<Type, List<EntityInfo>> SaveBundleToSaveMap(JObject saveBundle)
{
var _dynSaveBundle = (dynamic)saveBundle;
var _entitiesArray = (JArray)_dynSaveBundle.entities;
var _provider = new BreezeAdapter();
//Hack 1: Breeze.ContextProvider initializes a global JsonSerializer in its SaveChanges() method
//We are bypassing SaveChanges() and bootstrapping directly into SaveWorkState logic to generate our saveMap
//as such we need to init a serializer here and slipsteam it in via reflection (its got a private setter)
var _serializerSettings = BreezeConfig.Instance.GetJsonSerializerSettings();
var _bootstrappedJsonSerializer = JsonSerializer.Create(_serializerSettings);
//Hack 2:
//How to write to a private setter via reflection
//http://stackoverflow.com/questions/3529270/how-can-a-private-member-accessable-in-derived-class-in-c
PropertyInfo _jsonSerializerProperty = _provider.GetType().GetProperty("JsonSerializer", BindingFlags.Instance | BindingFlags.NonPublic);
//Hack 3: JsonSerializer property is on Breeze.ContextProvider type; not our derived EFContextProvider type so...
_jsonSerializerProperty = _jsonSerializerProperty.DeclaringType.GetProperty("JsonSerializer", BindingFlags.Instance | BindingFlags.NonPublic);
//Finally, we can init the JsonSerializer
_jsonSerializerProperty.SetValue(_provider, _bootstrappedJsonSerializer);
//saveWorkState constructor loads json entitiesArray into saveWorkState.EntityInfoGroups struct
var _saveWorkState = new SaveWorkState(_provider, _entitiesArray);
//BeforeSave logic loads saveWorkState.EntityInfoGroups metadata into saveWorkState.SaveMap
_saveWorkState.BeforeSave();
var _saveMap = _saveWorkState.SaveMap;
return _saveMap;
}
I looked into this. You don't actually need to make a change to the Breeze code to accomplish what you want. The ContextProvider is designed such that you can do just about whatever you want during save.
I'm curious: what "custom save handling" do you want to perform that you can't do today with the BeforeSave and AfterSave logic? I see in your "stopgap" code that you're calling BeforeSave on the SaveWorkState. What more do you need?
As an exercise, I wrote a NorthwindIBDoNotSaveContext that does what you want. Here's how it goes:
/// <summary>
/// A context whose SaveChanges method does not save
/// but it will prepare its <see cref="SaveWorkState"/> (with SaveMap)
/// so developers can do what they please with the same information.
/// See the <see cref="GetSaveMapFromSaveBundle"/> method;
/// </summary>
public class NorthwindIBDoNotSaveContext : EFContextProvider<NorthwindIBContext_CF>
{
/// <summary>
/// Open whatever is the "connection" to the "database" where you store entity data.
/// This implementation does nothing.
/// </summary>
protected override void OpenDbConnection(){}
/// <summary>
/// Perform your custom save to wherever you store entity data.
/// This implementation does nothing.
/// </summary>
protected override void SaveChangesCore(SaveWorkState saveWorkState) {}
/// <summary>
/// Return the SaveMap that Breeze prepares
/// while performing <see cref="ContextProvider.SaveChanges"/>.
/// </summary>
/// <remarks>
/// Calls SaveChanges which internally creates a <see cref="SaveWorkState"/>
/// from the <see param="saveBundle"/> and then runs the BeforeSave and AfterSave logic (if any).
/// <para>
/// While this works, it is hacky if all you want is the SaveMap.
/// The real purpose of this context is to demonstrate how to
/// pare down a ContextProvider, benefit from the breeze save pre/post processing,
/// and then do your own save inside the <see cref="SaveChangesCore"/>.
/// </para>
/// </remarks>
/// <returns>
/// Returns the <see cref="SaveWorkState.SaveMap"/>.
/// </returns>
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle)
{
SaveChanges(saveBundle); // creates the SaveWorkState and SaveMap as a side-effect
return SaveWorkState.SaveMap;
}
}
And here's how you could use it to get the SaveMap:
var saveMap = new NorthwindIBDoNotSaveContext().GetSaveMapFromSaveBundle(saveBundle);
Yes, it is "hacky", particularly if all you want is the SaveMap. But why do you just want the SaveMap?
We've designed the ContextProvider (and all of its sub-classes) such that you have free reign over the SaveChangesCore method. You could override that, further manipulate the SaveMap, then either delegate to the base implementation or do whatever else you have in mind for saving the entity data.
But while I don't see what you're after, it was not all that hard to extract the SaveChanges initialization logic into its own method.
So in the next release (after 1.5.2), you should find the following new method in the ContextProvider:
protected void InitializeSaveState(JObject saveBundle)
{
JsonSerializer = CreateJsonSerializer();
var dynSaveBundle = (dynamic)saveBundle;
var entitiesArray = (JArray)dynSaveBundle.entities;
var dynSaveOptions = dynSaveBundle.saveOptions;
SaveOptions = (SaveOptions)JsonSerializer.Deserialize(new JTokenReader(dynSaveOptions), typeof(SaveOptions));
SaveWorkState = new SaveWorkState(this, entitiesArray);
}
SaveChanges now calls that method before continuing on in its previous manner:
public SaveResult SaveChanges(JObject saveBundle, TransactionSettings transactionSettings = null) {
if (SaveWorkState == null || SaveWorkState.WasUsed) {
InitializeSaveState(saveBundle);
}
transactionSettings = transactionSettings ?? BreezeConfig.Instance.GetTransactionSettings();
...
}
Notice that SaveChanges won't call InitializeSaveState twice if you've already prepared the SaveWorkState by, say, calling InitializeSaveState externally and then called SaveChanges immediately thereafter. It also won't save twice with a "used" SaveWorkState.
The source is checked into github right now if you're interested.
You'll be able to get the SaveMap from a save bundle by adding this method to your sub-class of a ContextProvider as in this example:
public class NorthwindContextProvider: EFContextProvider<NorthwindIBContext_CF> {
...
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle) {
InitializeSaveState(saveBundle); // Sets initial EntityInfos
SaveWorkState.BeforeSave(); // Creates the SaveMap as byproduct of BeforeSave logic
return SaveWorkState.SaveMap;
}
...
}
Now you use that as follows:
var saveMap = ContextProvider.GetSaveMapFromSaveBundle(saveBundle);

How to Cleanup a ViewModel in Mvvm Light?

I have a list of items that goes to another page, That page is hooked up to a view model. In the constructor of this view model I have code that grabs data from the server for that particular item.
What I found is that when I hit the back button and choose another item fromt hat list and it goes to the other page the constructor does not get hit.
I think it is because the VM is now created and thinks it does not need a new one. I am wondering how do I force a cleanup so that a fresh one is always grabbed when I select from my list?
I faced the same issue, that's how i solved it.
Have a BaseView class, override OnNavigatedTo
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigatedToCommand != null && NavigatedToCommand.CanExecute(null))
NavigatedToCommand.Execute(null);
}
add DependencyProperty.
public static readonly DependencyProperty NavigatedToCommandProperty =
DependencyProperty.Register("NavigatedToCommand", typeof(ICommand), typeof(BaseView), null);
public ICommand NavigatedToCommand
{
get { return (ICommand)GetValue(NavigatedToCommandProperty); }
set { SetValue(NavigatedToCommandProperty, value); }
}
On the necessary pages, add to xaml (and, of course, inherit BaseView )
NavigatedToCommand="{Binding OnNavigatedToCommand}"
In the ViewModel, make command itself
public RelayCommand OnNavigatedToCommand
{ get { return new RelayCommand(OnNavigatedTo); } }
and implement method you want to call to update list
public async void OnNavigatedTo()
{
var result = await myDataService.UpdateMyList();
if (result.Status == OK)
MyList = result.List;
}
So, now, every time you navigate to page with list, inside of overriden OnNavigatedTo(), a NavigatedToCommand would be executed, which would execute OnNavigatedToCommand (which you set in xaml), which would call OnNavigatedTo, which would update your list.
A bit messy, but MVVM :)
EDIT: What about cleanings, they can be done in OnNavigatedFrom(), which works the same. Or OnNavigatingFrom(), which also can be useful in some cases.

Location aware code converted from C# to F#?

I have the following code in a WP7 app, and am starting to look at F#.. I can't find any GeoCoordinate examples, can anyone give me an idea of how this code would look in F#? Or point me to an example? I've had a look at some tutorials, books and Pluralsight, so think I am just starting to grasp the basics..but can't seem to get my head around this! All the examples I can seem to find are based around mathematical problem spaces. Any help or advice is much appreciated!
public partial class MainPage : PhoneApplicationPage
{
GeoCoordinateWatcher watcher;
// Constructor
public MainPage()
{
InitializeComponent();
SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
//Reinitialize the GeoCoordinateWatcher
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
watcher.MovementThreshold = 100;//distance in meters
//Add event handlers for StatusChanged and PositionChanged Events
watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
//Start data acquisition
watcher.Start();
//hide button
btnStart.Visibility = Visibility.Collapsed;
}
#region Event Handlers
/// <summary>
/// Handler for the StatusChanged event. This invokes MyStatusChanged on the UI thread
/// and passes the GeoPositionStatusChangedEventArgs
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => MyStatusChanged(e));
}
/// <summary>
/// Handler for the PositionChanged Event. This invokes MyPositionChanged on the UI thread and
/// passes the GeoPositionStatusChangedEventArgs
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => MyPositionChanged(e));
}
#endregion
/// <summary>
/// Custom method called from the PositionChanged event handler
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
void MyPositionChanged(GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//update the map to show the current location
GeoCoordinate geo = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
Location ppLoc = new Location(e.Position.Location.Latitude, e.Position.Location.Longitude);
mapMain.SetView(geo, 10);
//update pushpin location and show
MapLayer.SetPosition(ppLocation, ppLoc);
ppLocation.Visibility = System.Windows.Visibility.Visible;
}
/// <summary>
/// Custom method called from the StatusChanged event handler
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
void MyStatusChanged(GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
//the location service is disabled or unsupported, alert the user
tbStatus.Text = "Sorry we can't find you on this device";
break;
case GeoPositionStatus.Initializing:
//location service is initializing
//disable the start location button
tbStatus.Text = "Looking For you...";
break;
case GeoPositionStatus.NoData:
//location service is working but no data found, alert the user and enable the stop location button
tbStatus.Text = "can't find you yet...";
ResetMap();
break;
case GeoPositionStatus.Ready:
//location service is receiving data, show the current position and enable the stop location button
tbStatus.Text = "We found you!";
break;
}
}
void ResetMap()
{
Location ppLoc = new Location(0, 0);
GeoCoordinate goe = new GeoCoordinate(0.0,0.0);
mapMain.SetView(goe, 1);
//update pushpin location and show
MapLayer.SetPosition(ppLocation, ppLoc);
ppLocation.Visibility = System.Windows.Visibility.Collapsed;
}
}
I think that this is due to the fact that F# is touted as a language that you can process a large amount of information without being very verbose. While you can build small user interface elements using F# by calling relevant libraries, the intention is for you to build UI's with C# / ASP.NET/ etc. So, it wouldn't really make sense with your application because all you are doing is building a small UI and connecting events of that UI to a larger library of geoprocessing capabilities.
But if you wanted to collect information from that library (or a similar one) of all the points of interest nearby, then sort them according to distance from the user and his potential for 'liking' that point of interest based on some algorithm designed to compare a random point of interest with catagories or prior ratings then F# would be a good choice. You can rapidly describe those data structures, manipulate them, and return the result of it's processing back to your user interface.
This is why instructions such as the one shown here can be helpful. While very light on calling or creating a user interface (the C# code just display some text passed from the F# code), it can be used to create a backend for your phone application.
The sample you posted is a lot of code, so I don't expect that anybody will translate that to F# for you. Calling .NET functionality from F# is generally quite similar to how you'd call it from C# (at least initially, before you learn how to use some advanced F# patterns), so the translation should be pretty direct.
The F# version of code that initializes the GeoCoordinateWatcher is probably going to look like this:
let watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
watcher.MovementThreshold <- 100
// Add event handlers for StatusChanged and PositionChanged Events
watcher.StatusChanged.Add(fun eargs ->
MyStatusChanged(eargs) )
watcher.PositionChanged.Add(fun eargs ->
MyPositionChanged(eargs) )
// Start data acquisition
watcher.Start()
In general, F# has a couple of nice features that simplify user interface programming. As far as I know, there isn't a guide on developing Windows Phone applications in F#, specifically, but MSDN has a section that describes development of Silverlight applications, and most of the patterns will be the same:
Developing Client-Side Applications - Real World Functional programming on MSDN

Resources