I'd like to made an mobile application to do things after SMS is reveived.
I made an interface of the "SMS-Class" that defines all methods.
Then I made a Class in the Main Xamarin Forms project witch inherit from the interface (this way I can't miss to create any method).
Next I created a class in the Android part of the project witch should read (and more) SMS on receiving.
I called the mehtod of the android part with DependencyService from the class in the main project.
To manage the incomming SMS in android I did the following:
I made a method (in the Android project - "CheckForIncommingSMS") to register a BroadcastReceiver witch should be the calling class itself.
The mentioned method is called from the MainPage in the main project.
MainPage => SMSHelper.CheckForIncommingSMS() => Android Project => registring BroadcastReceiver for incomming SMS
Screenmessage when SMS received.
The Broadcast Receiver itself seems to work.
I tested it with other intentactions and it worked.
I found out that the OnReceive-method isn't called.
What did I wrong?
Thanks!
using System.Collections.Generic;
using App.Models;
namespace App.Services
{
public interface ISMSHelper
{
void CheckForIncommingSMS();
void Create(string sender, string receiver, string message);
void Send();
List<SMS> ReadAll();
SMS Get();
}
}
using System.Collections.Generic;
using App.Models;
using Xamarin.Forms;
// Class in Main Xamarin Forms Project
[assembly: Xamarin.Forms.Dependency(typeof(App.Services.SMSHelper))]
namespace App.Services
{
class SMSHelper : ISMSHelper
{
public void CheckForIncommingSMS()
{
DependencyService.Get<ISMSHelper>().CheckForIncommingSMS();
}
public void Create(string sender, string receiver, string message)
{
DependencyService.Get<ISMSHelper>().Create(sender, receiver, message);
}
public SMS Get()
{
return DependencyService.Get<ISMSHelper>().Get();
}
public List<SMS> ReadAll()
{
return DependencyService.Get<ISMSHelper>().ReadAll();
}
public void Send()
{
DependencyService.Get<ISMSHelper>().Send();
}
}
}
//Class in Xamarin Forms Project (Android)
using Android.App;
using Android.Content;
using Android.Provider;
using Android.Telephony;
using Android.Util;
using Android.Widget;
using Plugin.Messaging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using App.Models;
using App.Services;
using Xamarin.Forms;
[assembly: Xamarin.Forms.Dependency(typeof(App.Droid.SMSHelper))]
namespace App.Droid
{
[BroadcastReceiver(Enabled = true, Label = "SMS Receiver")]
[IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" })]
public class SMSHelper : BroadcastReceiver, ISMSHelper
{
private const string Tag = "SMSBroadcastReceiver";
private const string IntentAction = "android.provider.Telephony.SMS_RECEIVED";
SMS sms = new SMS();
public SMSHelper() {
/* https://blog.xamarin.com/cross-platform-messaging-for-ios-android-and-windows https://forums.xamarin.com/discussion/13682/broadcastreceiver-for-sms*/}
public void Create(string sender, string receiver, string message)
{
sms.Sender = sender;
sms.Receiver = receiver;
sms.Message = message;
}
public void Send()
{
if ((!sms.Receiver.Equals("")) && (!sms.Message.Equals("")))
{
var smsMessenger = CrossMessaging.Current.SmsMessenger;
if (smsMessenger.CanSendSmsInBackground)
{
smsMessenger.SendSmsInBackground(sms.Receiver, sms.Message);
}
}
}
public SMS Get()
{
return sms;
}
public List<SMS> ReadAll()
{
throw new System.NotImplementedException();
}
public override void OnReceive(Context context, Intent intent)
{
Log.Info(Tag, "Intent received: " + intent.Action);
if (intent.Action != IntentAction) return;
SmsMessage[] messages = Telephony.Sms.Intents.GetMessagesFromIntent(intent);
for (var i = 0; i < messages.Length; i++)
{
sms.Sender = messages[i].OriginatingAddress;
sms.Message = messages[i].MessageBody;
sms.Timestamp = DateTime.Now;
Toast.MakeText(context, sms.Message, ToastLength.Long).Show();
Debug.WriteLine("SMS ER");
}
Toast.MakeText(context, "sdfghhj", ToastLength.Long).Show();
}
public void CheckForIncommingSMS()
{
Debug.WriteLine("HALLO");
Forms.Context.RegisterReceiver(this, new IntentFilter(IntentAction));
}
}
}
Related
I am using xamarin forms for my app. I am using foreground service for my app to work on background. I kill the App and when I try to relaunch the App, I can't open the App. Here I am adding My service code below. Please see the code and give me suggestions to solve this issue.
My Service Code is below:
MyPrjService.cs:
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.Lang;
using System.Threading;
using System.Threading.Tasks;
using Android.Support.V4.App;
using Android;
using Android.Media;
using MyPrj.Interface;
using Xamarin.Forms;
using System.IO;
using Android.Net;
using MyPrj.Common;
using MyPrj.Services;
using Android.App;
using MyPrj.BusinessLogic;
using MyPrj.Helper;
using System;
using Plugin.Connectivity;
using MyPrj.Form;
using Android.Graphics;
namespace MyPrj.Droid.Services
{
[Service]
public class MyPrjService : Service
{
CancellationTokenSource _cts;
private static ILogger logger = DependencyService.Get<ILogManager>().GetLog();
public const string LOCATION_CHANNEL = "default";
NotificationManager manager;
NotificationCompat.Builder notification;
public override void OnCreate()
{
base.OnCreate();
manager = (NotificationManager)Forms.Context.GetSystemService("notification");
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
StartLocationServiceForeground();
return StartCommandResult.Sticky;
}
void StartLocationServiceForeground()
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var chan1 = new NotificationChannel(LOCATION_CHANNEL,
new Java.Lang.String("Primary"), NotificationImportance.High);
manager.CreateNotificationChannel(chan1);
notification = new NotificationCompat.Builder(Forms.Context, LOCATION_CHANNEL);
notification.SetOngoing(true)
//.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.icon))
.SetSmallIcon(Resource.Drawable.icon_transparent)
.SetContentTitle("MyPrj is running background")
.SetContentText("Tab for more information or to stop the app")
.SetColor(0x9c6114)
.SetPriority(NotificationCompat.PriorityHigh);
StartForeground(1, notification.Build());
}
}
catch(System.Exception ex)
{
}
}
public override void OnDestroy()
{
StopForeground(true);
if (manager!=null)
{
manager.CancelAll();
}
base.OnDestroy();
}
}
}
App.xaml.cs:
protected override async void OnSleep()
{
try
{
var context = Droid.MainActivity.Instance;
if (context != null)
{
context.StartServiceFromApp();
}
}
}
protected async override void OnResume()
{
var context = Droid.MainActivity.Instance;
if (context != null)
{
context.StopServiceFromApp();
}
}
MainActivity.cs:
public void StartServiceFromApp()
{
os 9
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var intent1 = new Intent(this, typeof(MyPrjService));
StartService(intent1);
}
}
public void StopServiceFromApp()
{
var intent1 = new Intent(this, typeof(ScoularService));
StopService(intent1);
}
I'm using Branch.io in a Xamarin Forms app, and my goal is to send push notifications through Azure Notification Hubs with Branch links in them to open the app and do something. I seem to have it all configured perfectly fine for iOS, but in Android I'm receiving the push notification and the correct activity is coming up, but InitSessionComplete is not called on my IBranchBUOSessionInterface object.
I'm suspecting the issue is in the way I'm creating the PendingIntent, but I could be totally wrong. Again, InitSessionComplete is called in every other circumstance except for when I receive a push notification with a Branch link.
All relevant code is below. Thanks!
MainApplication.cs
using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using BranchXamarinSDK;
using Plugin.CurrentActivity;
namespace MyCompany.MyApp.Droid
{
//You can specify additional application information in this attribute
[Application]
[MetaData("io.branch.sdk.auto_link_disable", Value = "false")]
[MetaData("io.branch.sdk.TestMode", Value = "true")]
[MetaData("io.branch.sdk.BranchKey", Value = "#string/branch_key")]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication (IntPtr handle, JniHandleOwnership transer)
: base (handle, transer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
RegisterActivityLifecycleCallbacks (this);
BranchAndroid.GetAutoInstance(ApplicationContext);
}
public override void OnTerminate ()
{
base.OnTerminate ();
UnregisterActivityLifecycleCallbacks (this);
}
public void OnActivityCreated (Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed (Activity activity)
{
}
public void OnActivityPaused (Activity activity)
{
}
public void OnActivityResumed (Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState (Activity activity, Bundle outState)
{
}
public void OnActivityStarted (Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped (Activity activity)
{
}
}
}
MainActivity.cs
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Util;
using BranchXamarinSDK;
using MyCompany.Shared.Droid.Modules;
using App = MyCompany.MyApp.Core.App;
namespace MyCompany.MyApp.Droid
{
[Activity (Theme = "#style/Custom.Holo",
Label = "MyApp",
Icon = "#drawable/icon",
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Portrait,
LaunchMode = LaunchMode.SingleTask,
MainLauncher = true)]
[IntentFilter(new[] { "android.intent.action.VIEW" },
Categories = new[] { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE" },
DataScheme = "myapp",
DataHost = "open")]
[IntentFilter(new[] { "android.intent.action.VIEW" },
Categories = new[] { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE" },
DataScheme = "https",
DataHost = "mycompanymyapp.test-app.link")]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsApplicationActivity, IBranchBUOSessionInterface
{
public const string Tag = "MainActivity";
private App _app;
internal static readonly string ChannelId = "MyCompany";
internal static readonly int NotificationId = 100;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
if (key == null) { continue; }
var value = Intent.Extras.GetString(key);
Log.Debug(Tag, "Key: {0} Value: {1}", key, value);
}
}
// Removes icon from android navbar
ActionBar.SetIcon(Android.Resource.Color.Transparent);
Xamarin.Forms.Forms.Init (this, savedInstanceState);
BranchAndroid.Debug = true;
var androidModule = new MyCompanyAndroidServicesModule ();
var app = new App (androidModule);
BranchAndroid.Init(this, GetString(Resource.String.branch_key), this);
LoadApplication(app);
_app = app;
}
#region IBranchSessionInterface implementation
public void InitSessionComplete(BranchUniversalObject buo, BranchLinkProperties blp)
{
_app.InitSessionComplete(buo, blp);
}
public void SessionRequestError(BranchError error)
{
_app.SessionRequestError(error);
}
#endregion
protected override void OnNewIntent(Intent intent)
{
Intent = intent;
}
}
}
MyCompany.MyApp.Core.cs
#region Libraries
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Autofac;
using BranchXamarinSDK;
using MyCompany.Core;
using MyCompany.Core.Factories;
using MyCompany.Core.Helpers;
using MyCompany.Core.Services;
using MyCompany.Core.ViewModels;
using MyCompany.MyApp.Core.ViewModels;
using Xamarin.Forms;
using Device = Xamarin.Forms.Device;
#endregion Libraries
namespace MyCompany.MyApp.Core
{
public class App : Application, ILoginApp, IBranchBUOSessionInterface
{
#region Variables
readonly IViewFactory _viewFactory;
readonly INavigationService _navService;
#endregion Variables
#region Constructor
public App (Module platformServiceModule = null)
{
var bootstrapper = new TimeAppBootstrapper ();
if (platformServiceModule != null)
bootstrapper.AddModule (platformServiceModule);
bootstrapper.Run ();
_viewFactory = bootstrapper.Container.Resolve<IViewFactory> ();
_navService = bootstrapper.Container.Resolve<INavigationService> ();
Page startView = new NavigationPage (_viewFactory.Resolve<LoginV2ViewModel> ()) {
BarBackgroundColor = Color.White,
BarTextColor = Color.FromRgb(34, 149, 236)
};
MainPage = startView;
}
#endregion Constructor
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
/// <summary>
/// Application developers override this method to perform actions
/// when the application resumes from a sleeping state.
/// </summary>
protected override void OnResume ()
{
}
#region IBranchSessionInterface implementation
public void InitSessionComplete(BranchUniversalObject buo, BranchLinkProperties blp)
{
if (blp.feature == "feature")
{
_navService.NavigateTo<FeatureViewModel>();
}
}
public void SessionRequestError(BranchError error)
{
var e = error;
}
#endregion
}
}
FirebaseMessagingService.cs
using Android.App;
using Android.Content;
using Android.Util;
using Firebase.Messaging;
using Xamarin.Essentials;
namespace Crowdkeep.Time.Droid.Services
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
const string TAG = "MyFirebaseMessagingService";
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(TAG, "From: " + message.From);
var notificationManager = NotificationManager.FromContext(this);
InitializeChannels(notificationManager);
if (IsProductionNotification(message))
{
SendProductionNotification(message, notificationManager, this);
}
else
{
SendTestNotification(message, notificationManager);
}
}
private static bool IsProductionNotification(RemoteMessage message)
{
return message.GetNotification() != null;
}
private void SendTestNotification(RemoteMessage message, NotificationManager notificationManager)
{
var notification = CreateNotification(message.Data["message"], message.Data["branch"], this);
notificationManager.Notify(0, notification);
}
private static void SendProductionNotification(RemoteMessage message, NotificationManager manager, Context context)
{
Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
var notification = CreateNotification(message.GetNotification().Body, "https://mycompanymyapp.test-app.link/feature", context);
manager.Notify(0, notification);
}
private static void InitializeChannels(NotificationManager manager)
{
if (DeviceInfo.Version.Major < 8 || manager.GetNotificationChannel(MainActivity.ChannelId) != null)
{
return;
}
var channel = new NotificationChannel(MainActivity.ChannelId, "Crowdkeep", NotificationImportance.Default)
{
Description = "Default Channel"
};
manager.CreateNotificationChannel(channel);
}
private static Notification CreateNotification(string messageBody, string link, Context context)
{
var pendingIntent = SetupNotificationIntent(link, context);
var notificationBuilder = new Notification.Builder(context, MainActivity.ChannelId)
.SetContentTitle("Message")
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(messageBody)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent)
.SetVisibility(NotificationVisibility.Public);
return notificationBuilder.Build();
}
private static PendingIntent SetupNotificationIntent(string link, Context context)
{
var intent = new Intent(context, typeof(MainActivity));
intent.SetFlags(ActivityFlags.ClearTop);
intent.PutExtra("branch", link);
intent.PutExtra("branch_force_new_session", true);
var pendingIntent = PendingIntent.GetActivity(context, MainActivity.NotificationId, intent, PendingIntentFlags.OneShot);
return pendingIntent;
}
}
}
Json Sent via Notification:
{"data":{"message":"Click this notification to go to the best feature of the app!", "branch": "https://mycompanymyapp.test-app.link/feature"}}
EDIT (1/7/2019)
I'm able to reproduce this in the Test Bed. I forked my own copy of Branch and added a branch called initsessioncomplete-push-notification-issue, which you can access here. I followed these instructions to setup Firebase and Azure Notification Hubs. In the solution, the only thing you'll have to modify are the two strings in the AppConstants class in the TestBed.Droid project, and add your google-services.json file to the root of the Droid project (the .csproj is already configured properly to read it).
As far as I can tell, I see a successful call being made to the Branch API when I click the push notification, but InitSessionComplete is never called.
Joon from Branch here. Can you double check that you are using the correct Branch Key in your AndroidManifest? I noticed that you are testing with a test Branch Link so you'll have to use your test Branch Key.
Using .net core I'm implementing class library to push bulk emails into Email server.Email server will consume my email list and gives feedback after complete.this will take 20-30 seconds.When I got feedback from email server I need to fire method.
I have referred this article to implement event handler.But when I debug it
EventHandler OnFeedbackReceived
parameter is null.see image below
This is class library code.
using System;
using System.Text;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Collections.Generic;
namespace OSH_EmailServerLibrary
{
public static class EmailMQServerLibrary
{
public static event EventHandler<EmailFeedbackEventArgs> OnFeedbackReceived;
public static void PushToMQ(List<EmailMessage> _emailList)
{
//
// Long RabbitMQ msg push code here
//
EmailMessageFeedback feedback = new EmailMessageFeedback { Description = "Completed", SuccessCount = 10, FailedCount = 0 };
SendFeedback(feedback);
Console.ReadLine();
}
private static EmailMessageFeedback SendFeedback(EmailMessageFeedback feedback)
{
if (OnFeedbackReceived != null)
{
OnFeedbackReceived(feedback, new
EmailFeedbackEventArgs(feedback));
}
return feedback;
}
}
public class EmailFeedbackEventArgs : EventArgs
{
public EmailFeedbackEventArgs(EmailMessageFeedback _feedback)
{
feedback = _feedback;
}
public EmailMessageFeedback feedback { get; set; }
}
}
This is how I user it in a console application
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using OSH_EmailServerLibrary;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace EmailServerSenderSampleConsole
{
class Program
{
static void Main(string[] args)
{
List<EmailMessage> _emailList = _emails.GetAllEmailToSend();
EmailMQServerLibrary.PushToMQ(_emailList);
EmailMQServerLibrary.OnFeedbackReceived += EmailMQServerLibrary_OnFeedbackReceived;
}
private static void EmailMQServerLibrary_OnFeedbackReceived(object sender, EmailFeedbackEventArgs e)
{
}
}
}
Finally Fixed my issue.Issue was not in my class library.Issue was I did subscribe event after PushToMQ() in my console application which is wrong..Thank you so much #Hans Passant helping me out.
here is my corrected answer.I think this will help others like me.
Class Library >>
using System;
using System.Text;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Collections.Generic;
namespace OSH_EmailServerLibrary
{
public static class EmailMQServerLibrary
{
public static event EventHandler<EmailFeedbackEventArgs> OnFeedbackReceived;
public static void PushToMQ(List<EmailMessage> _emailList)
{
//
// Long RabbitMQ msg push code here
//
EmailMessageFeedback feedback = new EmailMessageFeedback { Description = "Completed", SuccessCount = 10, FailedCount = 0 };
SendFeedback(feedback);
Console.ReadLine();
}
private static EmailMessageFeedback SendFeedback(EmailMessageFeedback feedback)
{
if (OnFeedbackReceived != null)
{
OnFeedbackReceived(feedback, new
EmailFeedbackEventArgs(feedback));
}
return feedback;
}
}
public class EmailFeedbackEventArgs : EventArgs
{
public EmailFeedbackEventArgs(EmailMessageFeedback _feedback)
{
feedback = _feedback;
}
public EmailMessageFeedback feedback { get; set; }
}
}
Console Application >>
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using OSH_EmailServerLibrary;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace EmailServerSenderSampleConsole
{
class Program
{
static void Main(string[] args)
{
List<EmailMessage> _emailList = _emails.GetAllEmailToSend();
EmailMQServerLibrary.OnFeedbackReceived +=
EmailMQServerLibrary_OnFeedbackReceived; //-- worked
EmailMQServerLibrary.PushToMQ(_emailList);
//EmailMQServerLibrary.OnFeedbackReceived +=
EmailMQServerLibrary_OnFeedbackReceived; -- Not working like this
}
private static void EmailMQServerLibrary_OnFeedbackReceived(object sender, EmailFeedbackEventArgs e)
{
}
}
}
I am trying to learn xaml and C# used in Xamarin Forms, and now I want to implement SQLite functionality.
I am simply trying to add a value into a SQL table but get the following error:
Unhandled Exception:
SQLite.SQLiteException: near ")": syntax error occurred
My Database connection interface is as follows:
using SQLite;
namespace TestSQLite
{
public interface IDatabaseConnection
{
SQLiteAsyncConnection GetConnection();
}
}
My Android specific Database Connection (iOS is identical) is as follows:
using SQLite;
using System.IO;
using TestSQLite;
using Xamarin.Forms;
[assembly: Dependency(typeof(DatabaseConnection))]
namespace TestSQLite
{
public class DatabaseConnection : IDatabaseConnection
{
public SQLiteAsyncConnection GetConnection()
{
var dbName = "TestDb.db3";
var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), dbName);
return new SQLiteAsyncConnection(path);
}
}
}
My MainPage (testpage) is as follows:
using SQLite;
using Xamarin.Forms;
namespace TestSQLite
{
public class ControlledDrugs
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Drug { get; set; }
public double Volume { get; set; }
}
public class Users
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
public partial class MainPage : ContentPage
{
private SQLiteAsyncConnection _connection;
public MainPage()
{
InitializeComponent();
_connection = DependencyService.Get<IDatabaseConnection>().GetConnection();
}
protected override async void OnAppearing()
{
await _connection.CreateTableAsync<ControlledDrugs>();
await _connection.CreateTableAsync<Users>();
var drugs = await _connection.Table<ControlledDrugs>().ToListAsync();
Drugslistview.ItemsSource = drugs;
var user = await _connection.Table<Users>().ToListAsync();
Userlistview.ItemsSource = user;
base.OnAppearing();
}
async void OnAdd(object sender, System.EventArgs e)
{
var user = UserInput.Text;
//The next step generates the error
await _connection.InsertAsync(user);
}
void OnUpdate(object sender, System.EventArgs e)
{
}
void OnDelete(object sender, System.EventArgs e)
{
}
}
}
As you can see, I have yet to progress to update or delete. Learning by Youtube and Stackoverflow snippets is painfully slow. But this error got me stumped.
Also, have NuGet package sqlite-net-pcl v1.5.166-beta installed Xamarin Visual Studio.
you are attempting to insert a string into the User table instead of a User object
var user = UserInput.Text;
// user is just a string
await _connection.InsertAsync(user);
instead you need to create a User object
var user = new Users { Name = UserInput.Text };
await _connection.InsertAsync(user);
I just started developing my brand new windows 8 application last week using mvvm light.I am familiar with mvvmlight WP7 navigation. How i can achieve the same in windows 8. Can any one suggest a better method to achieve the same in windows 8. I found a solution, where we override onnavigated events in vm and handle navigate to other page. But i think that method is obsolete. Any one please guide me with the proper implementation. Thanks in advance.
I understand this is not the exact answer you may be looking for, but this may give you some ideas to explore.
In my case, I'm not using MVVMLight - but my own simple MVVM implementation. I use the BindableBase class (which comes with the default VS 2012 RC templates) for property notifications. I imagine, you could use MVVMLight to give you some of the infrastructure, which you can complement with something like the below.
For navigation, I define an interface that looks like:
public interface INavigationService
{
void Navigate(Type type);
void Navigate(Type type, object parameter);
void EnsureNavigated(Type pageType, object parameter);
bool CanGoBack { get; }
bool CanGoForward { get; }
void GoBack();
void GoForward();
IView CurrentView { get; }
}
And implement it as follows:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
public class NavigationService : INavigationService
{
private readonly Frame _frame;
public NavigationService(Frame frame)
{
_frame = frame;
_frame.Navigated += OnFrameNavigated;
}
private void OnFrameNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
var view = e.Content as IView;
if (view == null)
return;
var navMsg = new NavigationMessage()
{
Sender = this,
NewView = view,
Parameter = e.Parameter,
NavigationMode = (int)e.NavigationMode
};
EventManager.Current.Publish(navMsg);
//Anything that the parent needs to be notified should happen in of after this method
var viewModel = view.ViewModel;
if (viewModel != null)
viewModel.Initialise(e.Parameter);
}
public void Navigate(Type pageType)
{
DisposePreviousView();
_frame.Navigate(pageType);
}
public void Navigate(Type pageType, object parameter)
{
DisposePreviousView();
_frame.Navigate(pageType, parameter);
}
private void DisposePreviousView()
{
var currentView = this.CurrentView;
var currentViewDisposable = currentView as IDisposable;
if (currentViewDisposable != null)
{
currentViewDisposable.Dispose();
currentViewDisposable = null;
} //view model is disposed in the view implementation
}
public void EnsureNavigated(Type pageType, object parameter)
{
var currentView = this.CurrentView;
if (currentView == null || currentView.GetType() != pageType)
{
Navigate(pageType, parameter);
}
}
public IView CurrentView
{
get { return _frame.Content as IView; }
}
public bool CanGoBack
{
get { return _frame != null && _frame.CanGoBack; }
}
public void GoBack()
{
// Use the navigation frame to return to the previous page
if (_frame != null && _frame.CanGoBack) _frame.GoBack();
}
public bool CanGoForward
{
get { return _frame != null && _frame.CanGoForward; }
}
public void GoForward()
{
// Use the navigation frame to return to the previous page
if (_frame != null && _frame.CanGoForward) _frame.GoForward();
}
}
IView:
public interface IView : IDisposable
{
IViewModel ViewModel { get; }
void Refresh();
}
IViewModel:
public interface IViewModel : INotifyPropertyChanged, IDisposable
{
void Initialise(object parameter);
string ViewTitle { get; }
void Refresh();
}
Finally, in the XAML page, define a Frame element:
<Frame x:Name="ContentFrame" />
And in the code-behind of the page: (this in the only ugly part in my opinion - but its hopefully not too bad):
var _navigationService = new NavigationService(this.ContentFrame);
You can now pass the _navigationService to the viewmodel. In my case I create the viewmodel in the code-behind of the page:
public HomePage()
{
this.InitializeComponent();
var _navigationService = NavigationService.GetFor(this.ContentFrame);
DataContext = new HomePageViewModel(_navigationService);
}
Hope this helps.
Read the article published in MSDN Magazine just recently by Laurent Bugnion himself on working with the MVVM Light Toolkit and Windows 8.
Towards the end of the article he explains exactly how to setup the NavigationService you need.
http://msdn.microsoft.com/en-us/magazine/jj651572.aspx
The NavigationService that was in MVVMLight has been migrated in a new package called WinRTBehaviors. You can also get EventToCommand in Win8nl, both from nuget. See my blog posted here:
Getting Started w/ MVVM Light for Windows 8, EventToCommand and Behaviors
http://blog.tattoocoder.com/2012/08/getting-started-w-windows-8-mvvm-light.html