ImagePicker MainActivity Instance is not defined - xamarin

Followed the below link in adding the ImagePicker
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/photo-picker
Here, for Android Implementation the issue is Instance is not defined in the MainActivity.cs
[assembly: Dependency(typeof(PicturePickerImplementation))]
namespace DependencyServiceSample.Droid
{
public class PicturePickerImplementation : IPicturePicker
{
public Task<Stream> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
// Start the picture-picker activity (resumes in MainActivity.cs)
MainActivity.Instance.StartActivityForResult(
Intent.CreateChooser(intent, "Select Picture"),
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Stream>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}
And the MainActivity doesn't have the Instance Field, Is there an alternate way to get the instance?
public class MainActivity : FormsAppCompatActivity
{
...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<Stream> PickImageTaskCompletionSource { set; get; }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
// Set the Stream as the completion of the Task
PickImageTaskCompletionSource.SetResult(stream);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}

To the MainActivity Class add this:
internal static MainActivity Instance { get; private set; }
protected override void OnResume()
{
Instance = this;
base.OnResume();
}
also thanks to #SushiHangover's answer here for describing how to initialize the Instance object.
The documentation is missing this, most likely.

You are missing a static variable definition and its assignment.
Add a static var named Instance to the MainActivity class:
public static Activity Instance;
and then in the OnResume override assign it:
protected override void OnResume()
{
Instance = this;
base.OnResume();
}

Related

How to migrate MVVMCross based Xamarin.android project into Intune managed one

I have a android project running smoothly, it uses MVVMCross at its core.
The problem came when I was asked manage the app protection policies with Intune.
Now Intune is forcing me to use their managed activity and all other managed namespaces provided by Intune SDK.
In that case, how I can proceed with it?
I tried changing activities base class to Intune's one, in hope to use general things provided by Mvvmcross, such as IOC, dependency injections.
I customised App startup as Intune wants that means there will not be any setup/app.cs class calls involvement.
So I launch Splash activity -> and it launches MainActivity, in MainActivity I am manually injecting all the Dependencies which I require.
Because all these syntaxes are throwing exception under Intune managed activities
example: Mvx.RegisterType<IDeviceInformation, DeviceInformation>();
Above throws exception.
How do I proceed with this migration keeping MVVMcross basic functionality intact?
There is a couple of solutions to that matter that I can think of.
If you only need the DI you can add another DI manager package and handle it from there which will be simpler than configuring Mvx to do that only.
If you need other capabilities of Mvx then you will have to do what Mvx does in its base classes and implement them taking into consideration setting the appropiate interfaces to your base classes.
In Android, in order to get the Setup and Activities working you'll have to:
Register your setup in your android Application file as done here
this.RegisterSetupType<TMvxAndroidSetup>();
Implement your own base activity that takes into consideration the implementation of IMvxEventSourceActivity such as here and also the MvxActivity like here in order to have the events and the data context / viewmodel handling
[Register("mvvmcross.platforms.android.views.base.MvxEventSourceActivity")]
public abstract class MvxEventSourceActivity
: Activity, IMvxEventSourceActivity
{
protected MvxEventSourceActivity()
{
}
protected MvxEventSourceActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected override void OnCreate(Bundle bundle)
{
CreateWillBeCalled.Raise(this, bundle);
base.OnCreate(bundle);
CreateCalled.Raise(this, bundle);
}
protected override void OnDestroy()
{
DestroyCalled.Raise(this);
base.OnDestroy();
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
NewIntentCalled.Raise(this, intent);
}
protected override void OnResume()
{
base.OnResume();
ResumeCalled.Raise(this);
}
protected override void OnPause()
{
PauseCalled.Raise(this);
base.OnPause();
}
protected override void OnStart()
{
base.OnStart();
StartCalled.Raise(this);
}
protected override void OnRestart()
{
base.OnRestart();
RestartCalled.Raise(this);
}
protected override void OnStop()
{
StopCalled.Raise(this);
base.OnStop();
}
public override void StartActivityForResult(Intent intent, int requestCode)
{
StartActivityForResultCalled.Raise(this, new MvxStartActivityForResultParameters(intent, requestCode));
base.StartActivityForResult(intent, requestCode);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
ActivityResultCalled.Raise(this, new MvxActivityResultParameters(requestCode, resultCode, data));
base.OnActivityResult(requestCode, resultCode, data);
}
protected override void OnSaveInstanceState(Bundle outState)
{
SaveInstanceStateCalled.Raise(this, outState);
base.OnSaveInstanceState(outState);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
DisposeCalled.Raise(this);
}
base.Dispose(disposing);
}
public event EventHandler DisposeCalled;
public event EventHandler<MvxValueEventArgs<Bundle>> CreateWillBeCalled;
public event EventHandler<MvxValueEventArgs<Bundle>> CreateCalled;
public event EventHandler DestroyCalled;
public event EventHandler<MvxValueEventArgs<Intent>> NewIntentCalled;
public event EventHandler ResumeCalled;
public event EventHandler PauseCalled;
public event EventHandler StartCalled;
public event EventHandler RestartCalled;
public event EventHandler StopCalled;
public event EventHandler<MvxValueEventArgs<Bundle>> SaveInstanceStateCalled;
public event EventHandler<MvxValueEventArgs<MvxStartActivityForResultParameters>> StartActivityForResultCalled;
public event EventHandler<MvxValueEventArgs<MvxActivityResultParameters>> ActivityResultCalled;
}
[Register("mvvmcross.platforms.android.views.MvxActivity")]
public abstract class MvxActivity
: MvxEventSourceActivity
, IMvxAndroidView
{
protected View _view;
protected MvxActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected MvxActivity()
{
BindingContext = new MvxAndroidBindingContext(this, this);
this.AddEventListeners();
}
public object DataContext
{
get { return BindingContext.DataContext; }
set { BindingContext.DataContext = value; }
}
public IMvxViewModel ViewModel
{
get
{
return DataContext as IMvxViewModel;
}
set
{
DataContext = value;
OnViewModelSet();
}
}
public void MvxInternalStartActivityForResult(Intent intent, int requestCode)
{
StartActivityForResult(intent, requestCode);
}
public IMvxBindingContext BindingContext { get; set; }
public override void SetContentView(int layoutResId)
{
_view = this.BindingInflate(layoutResId, null);
SetContentView(_view);
}
protected virtual void OnViewModelSet()
{
}
protected override void AttachBaseContext(Context #base)
{
if (this is IMvxSetupMonitor)
{
// Do not attach our inflater to splash screens.
base.AttachBaseContext(#base);
return;
}
base.AttachBaseContext(MvxContextWrapper.Wrap(#base, this));
}
private readonly List<WeakReference<Fragment>> _fragList = new List<WeakReference<Fragment>>();
public override void OnAttachFragment(Fragment fragment)
{
base.OnAttachFragment(fragment);
_fragList.Add(new WeakReference<Fragment>(fragment));
}
public List<Fragment> Fragments
{
get
{
var fragments = new List<Fragment>();
foreach (var weakReference in _fragList)
{
if (weakReference.TryGetTarget(out Fragment f))
{
if (f.IsVisible)
fragments.Add(f);
}
}
return fragments;
}
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
ViewModel?.ViewCreated();
}
protected override void OnDestroy()
{
base.OnDestroy();
ViewModel?.ViewDestroy(IsFinishing);
}
protected override void OnStart()
{
base.OnStart();
ViewModel?.ViewAppearing();
}
protected override void OnResume()
{
base.OnResume();
ViewModel?.ViewAppeared();
}
protected override void OnPause()
{
base.OnPause();
ViewModel?.ViewDisappearing();
}
protected override void OnStop()
{
base.OnStop();
ViewModel?.ViewDisappeared();
}
}
public abstract class MvxActivity<TViewModel>
: MvxActivity
, IMvxAndroidView<TViewModel> where TViewModel : class, IMvxViewModel
{
public new TViewModel ViewModel
{
get { return (TViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
}
Also you'll have to implement your own splash activity like here which implements the IMvxSetupMonitor and is the one who ends up calling the setup here by calling MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); and initializing a monitor.
[Register("mvvmcross.platforms.android.views.MvxSplashScreenActivity")]
public abstract class MvxSplashScreenActivity
: MvxActivity, IMvxSetupMonitor
{
protected const int NoContent = 0;
private readonly int _resourceId;
private Bundle _bundle;
public new MvxNullViewModel ViewModel
{
get { return base.ViewModel as MvxNullViewModel; }
set { base.ViewModel = value; }
}
protected MvxSplashScreenActivity(int resourceId = NoContent)
{
RegisterSetup();
_resourceId = resourceId;
}
protected MvxSplashScreenActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected virtual void RequestWindowFeatures()
{
RequestWindowFeature(WindowFeatures.NoTitle);
}
protected override void OnCreate(Bundle bundle)
{
RequestWindowFeatures();
_bundle = bundle;
var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext);
setup.InitializeAndMonitor(this);
base.OnCreate(bundle);
if (_resourceId != NoContent)
{
// Set our view from the "splash" layout resource
// Be careful to use non-binding inflation
var content = LayoutInflater.Inflate(_resourceId, null);
SetContentView(content);
}
}
private bool _isResumed;
protected override void OnResume()
{
base.OnResume();
_isResumed = true;
var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext);
setup.InitializeAndMonitor(this);
}
protected override void OnPause()
{
_isResumed = false;
var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext);
setup.CancelMonitor(this);
base.OnPause();
}
public virtual async Task InitializationComplete()
{
if (!_isResumed)
return;
await RunAppStartAsync(_bundle);
}
protected virtual async Task RunAppStartAsync(Bundle bundle)
{
if (Mvx.IoCProvider.TryResolve(out IMvxAppStart startup))
{
if(!startup.IsStarted)
{
await startup.StartAsync(GetAppStartHint(bundle));
}
else
{
Finish();
}
}
}
protected virtual object GetAppStartHint(object hint = null)
{
return hint;
}
protected virtual void RegisterSetup()
{
}
}
public abstract class MvxSplashScreenActivity<TMvxAndroidSetup, TApplication> : MvxSplashScreenActivity
where TMvxAndroidSetup : MvxAndroidSetup<TApplication>, new()
where TApplication : class, IMvxApplication, new()
{
protected MvxSplashScreenActivity(int resourceId = NoContent) : base(resourceId)
{
}
protected override void RegisterSetup()
{
this.RegisterSetupType<TMvxAndroidSetup>();
}
}
This will cover the basics I think.
Hope it helps you to get you to the right direction

How to get a callback from a custom renderer in xamarin forms

I Have a custom hybridWebView for android and ios to load the URL. What I required is to pass a callback to the content page once the URL has completed the loading. Code as below, help would much appreciate.
Content Page
public partial class ConveyancingLeadPage : ContentPage
{
DashboardViewModel viewmodel;
StorageService storage = new StorageService();
public ConveyancingLeadPage()
{
InitializeComponent();
GetUserAvatar();
}
protected async override void OnAppearing()
{
// I need the callback to be execute here
customView.weblink = viewmodel.BrokerData.config.conveyancing.listing_webview;
}
}
Android HybridView
[assembly: ExportRenderer(typeof(HCHybridWebview), typeof(HCHybridWebviewRendererAndroid))]
namespace HashChing.Droid.CustomRenderers
{
public class HCHybridWebviewRendererAndroid : ViewRenderer<HCHybridWebview, Android.Webkit.WebView>
{
Context _context;
public HCHybridWebviewRendererAndroid(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<HCHybridWebview> e)
{
base.OnElementChanged(e);
const string JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
if (Control == null)
{
//Do something
if (e.NewElement != null)
{
//Load URL
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
var hybridWebView = e.NewElement as HCHybridWebview;
if (hybridWebView != null)
{
hybridWebView.RefreshView += LoadUrl;
}
}
Load URL
public void LoadUrl(object sender, EventArgs e)
{
Control.LoadUrl(webView.weblink, headers);
}
Once the URL been loaded it will navigate to this method in the same class, and this is where I want to pass a callback TO my content page once the loading is completed inside the "OnPageFinished" method. Help would much appreciate.
public class JavascriptWebViewClient : WebViewClient
{
string _javascript;
public JavascriptWebViewClient(string javascript)
{
_javascript = javascript;
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
you could use MessagingCenter to send and get the callback:
in your ContentPage,for example Page1.xaml.cs
public Page1 ()
{
InitializeComponent ();
//here you could get the callback,and arg = "this is call back"
MessagingCenter.Subscribe<Page1,string>(this,"callback", (send, arg) =>
{
Console.WriteLine(arg);
});
}
then in your OnPageFinished method:
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
//send the callback content,Parameters can be defined by yourself
MessagingCenter.Send<Page1,string>(new Page1(), "callback","this is call back");
}
more information:MessagingCenter
create your own event LoadCompleted in your customrender and invoke it from your custom class once load is finish.
And in your JavascriptWebViewClient class you can subscribe to that event and do whatever you want do do at that time.
in case you need it: Events in c#
public partial class ConveyancingLeadPage : ContentPage
{
protected async override void OnAppearing()
{
customView.weblink = viewmodel.BrokerData.config.conveyancing.listing_webview;
// I need the callback to be execute here
customView.LoadCompleted+=LoadCompleted;
}
}

How to use an interface

I'm trying to build my first xamarin app, which I'm building using forms. One of the features of the app is sending users locations and have to do that even if the app is in the background. So I came across James Montemagno's GeolocatorPlugin, which promised to do just that.
As the documentation was not that clear on how to implement his plugin in the background I looked through the projects closed issues and found a guy which gave an example of a simple case of using the plugin with a service. (https://github.com/jamesmontemagno/GeolocatorPlugin/issues/272)
I've adopted the code and created the service. The service are using an interface to start the service and now my problem is how to make use of the interface to make the service run.
In my shared project I put the interface and the viewmodel and in xamarin.android project I put the service.
The interface - IGeolocationBackgroundService:
public interface IGeolocationBackgroundService {
void StartService();
void StartTracking();
}
The viewmodel - GeolocatorPageViewModel:
public class GeolocatorPageViewModel
{
public Position _currentUserPosition { get; set; }
public string CoordinatesString { get; set; }
public List<string> userPositions { get; set; }
public ICommand StartTrackingCommand => new Command(async () =>
{
if (CrossGeolocator.Current.IsListening)
{
await CrossGeolocator.Current.StopListeningAsync();
}
CrossGeolocator.Current.DesiredAccuracy = 25;
CrossGeolocator.Current.PositionChanged += Geolocator_PositionChanged;
await CrossGeolocator.Current.StartListeningAsync(
TimeSpan.FromSeconds(3), 5);
});
private void Geolocator_PositionChanged(object sender, PositionEventArgs e)
{
var position = e.Position;
_currentUserPosition = position;
var positionString = $"Latitude: {position.Latitude}, Longitude: {position.Longitude}";
CoordinatesString = positionString;
Device.BeginInvokeOnMainThread(() => CoordinatesString = positionString);
userPositions.Add(positionString);
Debug.WriteLine($"Position changed event. User position: {CoordinatesString}");
}
}
The service - GeolocationService:
[assembly: Xamarin.Forms.Dependency(typeof(GeolocationService))]
namespace MyApp.Droid.Services
{
[Service]
public class GeolocationService : Service, IGeolocationBackgroundService
{
Context context;
private static readonly string CHANNEL_ID = "geolocationServiceChannel";
public GeolocatorPageViewModel ViewModel { get; private set; }
public override IBinder OnBind(Intent intent)
{
return null;
}
public GeolocationService(Context context)
{
this.context = context;
CreateNotificationChannel();
}
private void CreateNotificationChannel()
{
NotificationChannel serviceChannel = new NotificationChannel(CHANNEL_ID,
"GeolocationService", Android.App.NotificationImportance.Default);
NotificationManager manager = context.GetSystemService(Context.NotificationService) as NotificationManager;
manager.CreateNotificationChannel(serviceChannel);
}
//[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
var newIntent = new Intent(this, typeof(MainActivity));
newIntent.AddFlags(ActivityFlags.ClearTop);
newIntent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, newIntent, 0);
var builder = new Notification.Builder(this, CHANNEL_ID);
var notification = builder.SetContentIntent(pendingIntent)
.SetSmallIcon(Resource.Drawable.ic_media_play_light)
.SetAutoCancel(false)
.SetTicker("Locator is recording")
.SetContentTitle("GeolocationService")
.SetContentText("Geolocator is recording for position changes.")
.Build();
StartForeground(112, notification);
//ViewModel = new GeolocatorPageViewModel();
return StartCommandResult.Sticky;
}
public void StartService()
=> context.StartService(new Intent(context, typeof(GeolocationService)));
public void StartTracking()
{
ViewModel = new GeolocatorPageViewModel();
ViewModel.StartTrackingCommand.Execute(null);
}
}
}
So be clear, I need to start the service and I'm not used to interfaces, so how do I call the interface?
use DependencyService to get a reference to your service and then start it
var svc = DependencyService.Get<IGeolocationBackgroundService>();
svc.StartService();
svc.StartTracking();

MVC3, Unity Framework and Per Session Lifetime Manager Issue

In a simple word I try to create Lifetime manager for Unity framework by using Http Session in my MVC3 project. My sample implementation of lifetime manager is:
public class UnityPerSessionLifetimeManager : LifetimeManager
{
private string sessionKey;
private HttpContext ctx;
public UnityPerSessionLifetimeManager(string sessionKey)
{
this.sessionKey = sessionKey;
this.ctx = HttpContext.Current;
}
public override object GetValue()
{
return this.ctx.Session[this.sessionKey];
}
public override void RemoveValue()
{
this.ctx.Items.Remove(this.sessionKey);
}
public override void SetValue(object newValue)
{
this.ctx.Session[this.sessionKey] = newValue;
}
}
In my global.asax.cs I replaced default controller factory with my own UnityControllerFactory
public class UnityControllerFactory : DefaultControllerFactory
{
private IUnityContainer container;
public UnityControllerFactory(IUnityContainer container)
{
this.container = container;
this.RegisterServices();
}
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType != null)
{
return this.container.Resolve(controllerType) as IController;
}
return null;
}
private void RegisterServices()
{
this.container.RegisterType<IMyType, MyImpl>(new UnityPerSessionLifetimeManager("SomeKey"));
}
}
}
I set breakpoints on each function of UnityPerSessionLifetimeManager class, I noticed that when controller factory tries to solve dependencies of my controller, the HttpContext.Session is actually null, so the code fails retrieve from session or save to session.
Any idea why session is null at this stage?
My mistake, I should change code of UnityPerSessionLifetimeManager class to be
public class UnityPerSessionLifetimeManager : LifetimeManager
{
private string sessionKey;
public UnityPerSessionLifetimeManager(string sessionKey)
{
this.sessionKey = sessionKey;
}
public override object GetValue()
{
return HttpContext.Current.Session[this.sessionKey];
}
public override void RemoveValue()
{
HttpContext.Current.Session.Remove(this.sessionKey);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Session[this.sessionKey] = newValue;
}
}
because when the constructor was called to register type, session state is not ready yet and I already assigned http context of that time to a variable. But in later Get/Set functions session state is ready.

NavigationService throws NullReferenceException

Using MVVM Light, I'm trying to develop a rather simple WP7 application. I've run into a problem using the navigation service. I can navigate to a page, but after pressing the back button I can't navigate to the same page again. NavigationService throws a NullReferenceException.
I have implemented my navigation using Messaging from the GalaSoft.MvvmLight.Messaging namespace. All my views inherits from a customized PhoneApplicationPage base class that registrers a listener on "NavigationRequest":
public class PhoneApplicationPage : Microsoft.Phone.Controls.PhoneApplicationPage
{
public PhoneApplicationPage() : base()
{
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => NavigationService.Navigate(uri));
}
}
From my view models I post Uri's to this listener:
SendNavigationRequestMessage(new Uri("/View/AppSettingsView.xaml", UriKind.Relative));
Like i said, this works except when navigating after pressing the Back button.
Why is this and how can I solve it?
Is there a better way to implement navigation using MVVM Light?
I'm using MVVM Light as well. I have a class called PageConductor, which is based on what John Papa (Silverlight MVP) from Microsoft uses. Here's the PageConductor Service I use
public class PageConductor : IPageConductor
{
protected Frame RootFrame { get; set; }
public PageConductor()
{
Messenger.Default.Register<Messages.FrameMessage>(this, OnReceiveFrameMessage);
}
public void DisplayError(string origin, Exception e, string details)
{
string description = string.Format("Error occured in {0}. {1} {2}", origin, details, e.Message);
var error = new Model.Error() { Description = description, Title = "Error Occurred" };
Messenger.Default.Send(new Messages.ErrorMessage() { Error = error });
}
public void DisplayError(string origin, Exception e)
{
DisplayError(origin, e, string.Empty);
}
private void OnReceiveFrameMessage(Messages.FrameMessage msg)
{
RootFrame = msg.RootFrame;
}
private void Go(string path, string sender)
{
RootFrame.Navigate(new Uri(path, UriKind.Relative));
}
public void GoBack()
{
RootFrame.GoBack();
}
}
In my MainPage.xaml.cs constructor, I have this, which creates an instance of my ContentFrame in my PageConductor service.:
Messenger.Default.Send(new Messages.FrameMessage() { RootFrame = ContentFrame });
I then use dependency injection to instantiate an instance of my PageConductor Service into my MainPage ViewModel. Here is my MainViewModel class:
protected Services.IPageConductor PageConductor { get; set; }
public RelayCommand<string> NavigateCommand { get; set; }
public MainViewModel(Services.IPageConductor pageConductor)
{
PageConductor = pageConductor;
RegisterCommands();
}
private void RegisterCommands()
{
NavigateCommand = new RelayCommand<string>(
(source) => OnNavigate(source));
}
private void OnNavigate(string sender)
{
PageConductor.GoToView(sender, "main");
}
Notice the instance of my PageConductorService as a parameter in my MainViewModel constructor method. I pass this in via my ViewModelLocator:
private readonly TSMVVM.Services.ServiceProviderBase _sp;
public ViewModelLocator()
{
_sp = Services.ServiceProviderBase.Instance;
CreateMain(_sp);
}
#region MainPageViewModel
public static MainViewModel MainStatic
{
get
{
Services.ServiceProviderBase SP = Services.ServiceProviderBase.Instance;
if (_main == null)
{
CreateMain(SP);
}
return _main;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return MainStatic;
}
}
public static void ClearMain()
{
_main.Cleanup();
_main = null;
}
public static void CreateMain(Services.ServiceProviderBase SP)
{
if (_main == null)
{
_main = new MainViewModel(SP.PageConductor);
}
}
#endregion
For further reference, my Messages.FrameMessage class is simply:
internal class FrameMessage
{
public Frame RootFrame { get; set; }
}
I've had no issues with forward/back buttons.

Resources