I am having trouble with local notification on Android using Xamarin. The local notification shows every time the app is in the foreground or open. If I reboot the notification does not show. There is a RECEIVE_BOOT_COMPLETED BroadcastReceiver but I do not know if it is firing on reboot.
I cannot figure out what is wrong. I have tripled checked everything and feel like something small is missing.
Edit: I think I have it worked out for notifications on the app being closed thanks to this thread: Broadcast receiver not working when app is closed. Essentially, if debugging, the app needs to be reopened after closing it so that the receiver is called.
I used this doc to setup local notifications: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
AndroidManifest.cs
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="5" android:versionName="1.0.2.1" package="com.business.myapp" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:label="MyApp" android:theme="#style/MainTheme" android:supportsRtl="true" android:icon="#mipmap/icon"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
</manifest>
AlarmHandler.cs
using Android.Content;
using MyApp.Business.Services;
using MyApp.Global;
using MyApp.Utils;
using Serilog;
using System;
namespace MyApp.Droid
{
/// <summary>
/// Handle notification receiving.
///
/// https://github.com/xamarin/xamarin-forms-samples/blob/main/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs
/// </summary>
[BroadcastReceiver(
Enabled = true,
Exported = true,
Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
/// <summary>
/// Called when a notification is shown.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
// if (intent?.Extras != null)
// {
if (!Settings.DailyNotificationEnabled) return;
// do not show notification if it is not time based on the settings time value.
// this handles cases where the user changes the notification time multiple times.
/* Disabled on 3/6/2022 to see if this helps notifications.
var timeSpan = DateTime.Now - NotificationDateTime.GetDailyNotificationDateTime();
var absTotalSeconds = Math.Abs(timeSpan.TotalSeconds);
if (absTotalSeconds > 59 && absTotalSeconds < 86399) return;
*/
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
title = Constants.NotificationOffllineTitle;
message = Constants.NotificationOffllineMessage;
try
{
var votdHttpRequest = new VotdHttpRequest();
var votdApiDto = votdHttpRequest.MakeApiCall(DateTime.Now, Settings.TwoLetterISOLanguageName);
title = $"{DateTime.Now.ToShortDateString()} {votdApiDto.#ref} ({votdApiDto.ver.ToUpper()})";
var scripture = Settings.DisplayKjvVersion ? votdApiDto.en_scrip_kjv : votdApiDto.en_scrip;
message = Remove.RecursiveHtmlDecode(scripture);
}
catch (Exception ex)
{
Log.Error(ex, "Failed recieving notification.");
}
AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
manager.Show(title, message);
// }
}
}
}
AndroidNotificationManager.cs
using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;
using MyApp.Notifications;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;
[assembly: Dependency(typeof(MyApp.Droid.AndroidNotificationManager))]
namespace MyApp.Droid
{
/// <summary>
/// Daily notification manager.
/// </summary>
public class AndroidNotificationManager : INotificationManager
{
#region Private Fields
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
bool channelInitialized = false;
int messageId = 0;
int pendingIntentId = 0;
NotificationManager manager;
#endregion
#region Public Properties
public const string TitleKey = "title";
public const string MessageKey = "message";
public event EventHandler NotificationReceived;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
#endregion
#region Constructor
#endregion
#region Private Methods
/// <summary>
/// Create the notification channel.
/// </summary>
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
/// <summary>
/// Get the time of notification in UTC ticks.
/// </summary>
/// <param name="notifyTime"></param>
/// <returns></returns>
long GetNotifyTime(DateTime notifyTime)
{
DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(notifyTime);
double epochDiff = (new DateTime(1970, 1, 1) - DateTime.MinValue).TotalSeconds;
long utcAlarmTime = utcTime.AddSeconds(-epochDiff).Ticks / 10000;
return utcAlarmTime; // milliseconds
}
#endregion
#region Public Methods
/// <summary>
/// Initialize the notification channel
/// </summary>
public void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
/// <summary>
/// Schedule a notification.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="notifyTime">Time of notification.</param>
public void SendNotification(string title, string message, int id, DateTime? notifyTime = null)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
if (notifyTime != null)
{
var intent = CreateIntent(id);
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
intent.SetIdentifier(id.ToString());
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.Immutable);
long triggerTime = GetNotifyTime(notifyTime.Value);
long millisecondsRepeating = AlarmManager.IntervalDay;
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.SetRepeating(AlarmType.RtcWakeup, triggerTime, millisecondsRepeating, pendingIntent);
}
else
{
Show(title, message);
}
}
/// <summary>
/// Called when a notifiation is received.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void ReceiveNotification(string title, string message)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
};
NotificationReceived?.Invoke(null, args);
}
/// <summary>
/// Display the notification in the notification area.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void Show(string title, string message)
{
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
PendingIntent pendingIntent = PendingIntent.GetActivity(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var textStyle = new NotificationCompat.BigTextStyle();
textStyle.BigText(message);
textStyle.SetSummaryText(title);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
// .SetContentTitle(title) // not required with big text style
// .SetContentText(message) // not required with big text style
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.icon_votd))
.SetSmallIcon(Resource.Drawable.icon_votd)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate)
.SetStyle(textStyle);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
builder.SetVisibility((int)NotificationVisibility.Public);
Notification notification = builder.Build();
notification.Flags = NotificationFlags.AutoCancel;
manager.Notify(messageId++, notification);
}
/// <summary>
/// Delete a notification.
/// </summary>
/// <param name="id"></param>
public void DeleteNotification(int id)
{
Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler));
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
id, intent,
PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable);
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.Cancel(pendingIntent);
}
private Intent CreateIntent(int id)
{
return new Intent(Android.App.Application.Context, typeof(AlarmHandler)).SetAction("LocalNotifierIntent" + id);
}
public void CancelAlarm(int id)
{
var intent = CreateIntent(id);
var pendingIntent = PendingIntent.GetBroadcast(
Android.App.Application.Context,
0,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
alarmManager.Cancel(pendingIntent);
var notificationManager = NotificationManagerCompat.From(Android.App.Application.Context);
notificationManager.CancelAll();
notificationManager.Cancel(id);
}
private AlarmManager GetAlarmManager()
{
var alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
return alarmManager;
}
#endregion
}
}
BootReciever.cs
using Android.App;
using Android.Content;
using MyApp.Notifications;
using MyApp.Utils;
using System;
using Xamarin.Forms;
namespace MyApp.Droid.Notifications
{
/// <summary>
/// Used to reschedule notifications on boot. Android notifications are deleted when the OS is rebooted.
/// </summary>
[BroadcastReceiver(
Enabled = true,
Label = "Reboot complete receiver",
Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
/// <summary>
/// Called on boot.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
//if (intent.Action == "android.intent.action.BOOT_COMPLETED")
//{
// ScheduleDailyNotification();
//}
ScheduleDailyNotification();
}
/// <summary>
/// Schedule the daily notification.
/// </summary>
private void ScheduleDailyNotification()
{
if (Settings.DailyNotificationEnabled)
{
var notificationManager = DependencyService.Get<INotificationManager>();
var notificationDateTime = NotificationDateTime.GetDailyNotificationDateTime();
notificationManager.SendNotification(
Global.Constants.NotificationOffllineTitle,
Global.Constants.NotificationOffllineMessage,
Global.Constants.DailyNotificationId,
//DateTime.Now.AddSeconds(60));
notificationDateTime);
}
}
}
}
I hate to delete a question from the internet.
The issue on boot received was using dependency injection in the boot receiver. Exception: You must call Xamarin.Forms.Forms.Init(); prior to using this property. I just instantiated the class instead of using DI for this instance.
Here is what worked for me on the issue with the notification not showing when the app was closed: https://stackoverflow.com/a/60197247/814891
After registering my BroadcastReceiver (BR) statically in the
manifest, applying the proper intent filters, using JobIntentService
(and registering it in the manifest) to handle the work that was
called from my BR, I was still getting inconsistent results.
Once all of what I listed above has been done you should be able to
send an ADB command to activate your broadcast and process the work in
your service even if the app is closed. This was working for me some
of the time, but not all of the time.
This article describes limitation to BRs. "As of Android 3.1 the
Android system excludes all receiver from receiving intents by default
if the corresponding application has never been started by the user or
if the user explicitly stopped the application via the Android menu"
(AKA a user executes Force Stop)
When I start the app by debugging it, then swipe it closed on my
device, my ADB command never activates my BR. However, after my
debugging session is over, when I open up the app on my device and
swipe it closed, I can activate my BR through ADB commands.
This occurs because when you debug an application, then manually swipe
it closed on the device, Android considers this a Force Stop hence why
my BR cannot be activated until I re-open the app on the device
without debugging.
Scoured the internet for hours, and wasn't able to find my solution,
so I thought I'd post it here just in case some poor unfortunate soul
is encountering the same weird functionality I was.
Happy coding :)
Related
Situation:
I have a dll and a program which should use this dll with Late binding.
Every method is working are also events. For excample there is a logging event. I want to an own logging method in the program but here is my problem. I don't no how.
dll log class (c#):
/// <summary>
/// log helper class.
/// </summary>
public static class Log {
// ######### Enum #############################################################################################
/// <summary>
/// Log level enum.
/// </summary>
public enum LogLevel {
None = -1,
Debug = 0,
Info = 1,
Warn = 2,
Error = 3,
Fatal = 4
}
// ######### Events ###########################################################################################
/// <summary>
/// log helper class log event.
/// </summary>
public static event EventHandler<HelperLogEventArgs> HelperLog;
// ######### Constructor ######################################################################################
/// <summary>
/// Default constructor
/// </summary>
static Log() {
HelperLog += DebugLog;
}
// ######### Methods ##########################################################################################
/// <summary>
/// log helper class log event handling.
/// </summary>
/// <param name="e">Log event arguments</param>
public static void OnHelperLog(HelperLogEventArgs e) {
EventHandler<HelperLogEventArgs> handler = HelperLog;
if (handler != null) {
handler(null, e);
}
}
/// <summary>
/// Log a message to the debug console.
/// </summary>
/// <param name="sender">Event sender</param>
/// <param name="e">Log event arguments</param>
private static void DebugLog(object sender, HelperLogEventArgs e) {
Debug.WriteLine(String.Format(" [{0}]: {1}", e.logLevel.ToString(), e.message));
}
/// <summary>
/// Log a message.
/// </summary>
/// <param name="level">Log level</param>
/// <param name="message">Log message</param>
public static void Log(LogLevel level, string message) {
HelperLogEventArgs args = new HelperLogEventArgs();
args.logLevel = level;
args.logTime = DateTime.Now;
args.message = message;
OnHelperLog(args);
}
// ######### Help Classes #####################################################################################
/// <summary>
/// log helper class event argument class.
/// </summary>
public class HelperLogEventArgs : EventArgs {
/// <summary>
/// Log message log level.
/// </summary>
public Log.LogLevel logLevel;
/// <summary>
/// Log message text.
/// </summary>
public string message;
/// <summary>
/// Log message log timestamp.
/// </summary>
public DateTime logTime;
}
}
And now I have a Wrapper class in the external program with loads the dll unfortunately in another language (vb.Net.):
If _oAssembly Is Nothing Then
_oAssembly = Assembly.LoadFrom(sTfsHelperDllPath)
End If
' This will not throw an exception but return nothing.
_oTFSHelper = _oAssembly.CreateInstance("TFSHelper.TFSHelper")
If _oTFSHelper Is Nothing Then
' Throw manually an exception on error.
Throw New Exception("## TFSHelper could not be loaded. ##")
End If
Now can somebody show me how I write and add a logging method/ event hander in the external programm with will called then the event in the ddl is raised?
One possible solution is to use Reflection to add an event:
' Add event Handler
Dim method As MethodInfo = Me.GetType().GetMethod("<NewEventHandlerName>", BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Public)
Dim eventInfo As EventInfo = _oTFSHelper.GetType().GetEvent("<EventName>")
Dim type As Type = eventInfo.EventHandlerType
Dim handler As [Delegate] = [Delegate].CreateDelegate(type, Me, method)
eventInfo.AddEventHandler(_oTFSHelper, handler)
I have a set of integration tests executed by MsTest around OWIN, using the OWIN startup method for self host.
The tests are quite simple and use the following pattern:
WebApp.Start<Startup>(url: appAddress);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders
.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(uri).Result;
When I run the test on our local DEV machines, they are green.
When we run the test from Visual Studio inside our Build Machine, they are green.
If they run from the queue, few days ago, we started to get this annoying error from OWIN:
*** OWIN STARTED ***
{"Message":"An error has occurred.",
"ExceptionMessage":"Multiple types were found that match the controller named 'xxx'.
This can happen if the route that services this request ('odata/v1/{*odataPath}') found multiple controllers defined with the same name but differing namespaces, which is not supported.
The request for 'xxx' has found the following matching controllers:
namespace.V1.Controllers.xxxController
namespace.V1.Controllers.xxxController",
"ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.SelectController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}
And this is strange because we use Windsor to resolve the controllers and we also print out the registrations in Windsor and of course there is only one controller with that name. We have this issue on all the controllers only if the code is executed within MsBuild ... Inside Visual Studio of the Build Machine or in production, it works.
Could be that the error is something else but swallowed by OWIN?
I was getting the same error and In my case it was a combination of factors:
I'm using Nunit and Owin for selfhosting.
In the setup of the tests I start web server:
_server = WebApp.Start<Startup>(new StartOptions(baseAddress));
In the teardown of the tests I kill it:
_server.Dispose();
One of my tests was using TestCaseSource which is executed before the setup:
[TestCaseSource(typeof(TestData), "TestDataSource")]
public void Test_With_Source(TestData testcase)
Down the line, inside TestData.TestDataSource I was loading the assembly that contains the controllers to get some route information:
loadedAssembly = Assembly.LoadFile(dll); //First load of the assembly
At the point where the the web server was started (on the setup) the assembly was loaded once already (by the TestCaseSource) so I end up with the same assembly twice and web api complaining about a duplication of the same controller.
My fix was to remove the Assembly.LoadFile(dll) by:
typeof(BaseController).Assembly
That made the assembly containing the BaseController not been loaded twice.
We solved the problem in our tests by hosting the OWIN project within IIS express
public class IisExpressRunner : IDisposable
{
/// <summary>
/// Stores whether this instance has been disposed.
/// </summary>
private Boolean _isDisposed;
/// <summary>
/// Stores the IIS Express process.
/// </summary>
private Process _process;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Starts IIS Express using the specified directory path and port.
/// </summary>
/// <param name="configPath">
/// The directory path.
/// </param>
/// <param name="siteName">
/// The port.
/// </param>
public void Start(String configPath, string siteName)
{
String iisExpressPath = DetermineIisExpressPath();
String arguments = String.Format(
CultureInfo.InvariantCulture, "/config:\"{0}\" /site:{1}", configPath, siteName);
ProcessStartInfo info = new ProcessStartInfo(iisExpressPath)
{
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = true,
LoadUserProfile = true,
CreateNoWindow = false,
UseShellExecute = false,
Arguments = arguments
};
Thread startThread = new Thread(() => StartIisExpress(info))
{
IsBackground = true
};
startThread.Start();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(Boolean disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
// Free managed resources
if (_process.HasExited == false)
{
_process.CloseMainWindow();
}
_process.Kill();
_process = null;
}
// Free native resources if there are any
_isDisposed = true;
}
/// <summary>
/// Determines the IIS express path.
/// </summary>
/// <returns>
/// A <see cref="String"/> instance.
/// </returns>
private static String DetermineIisExpressPath()
{
String iisExpressPath;
if (Environment.Is64BitOperatingSystem)
{
iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
}
else
{
iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
}
iisExpressPath = Path.Combine(iisExpressPath, #"IIS Express\iisexpress.exe");
return iisExpressPath;
}
/// <summary>
/// Starts the IIS express.
/// </summary>
/// <param name="info">
/// The info.
/// </param>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Required here to ensure that the instance is disposed.")]
private void StartIisExpress(ProcessStartInfo info)
{
try
{
_process = Process.Start(info);
_process.WaitForExit();
}
catch (Exception)
{
Dispose();
}
}
}
I'm trying to build a UserControl for a Windows Phone app using Caliburn Micro inside the control. The bootstrapper is usually setup in the App resources
<Application.Resources>
<local:AppBootstrapper x:Key="bootstrapper" />
</Application.Resources>
I try to do this in the Xaml of the user control.
<UserControl.Resources>
<local:AppBootstrapper x:Key="bootstrapper" />
</UserControl.Resources>
But this throws an exception during initialization when the component is loaded. The LoadComponent call throws the exception: "A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.Phone.ni.dll"
Where and when should the bootstrapper be initialised?
It's not intended to be used in a UserControl's resources, so I can't guarantee any good or bad behavior. The bootstrapper should be used in the application resources or you can instantiate it directly in code. Try creating it in the user control's ctor, just after the InitializeComponent call.
Since you are placing a bootstrapper on a UserControl it seems that the PhoneApplicationService is probably already instantiated - have you tried putting the boostrapper in the app resources section?
The CM source shows that CM creates a new instance of PhoneApplicationService when the bootstrapper initialises and that looks like the issue, the root of your application must have already created an instance.
Is there a reason you can't use the boostrapper in the standard way (in app resources)? Does your App.xaml contain an initialiser for PhoneApplicationService?
Edit:
CM source where you are getting the error is in PrepareApplication. e.g.
protected override void PrepareApplication() {
base.PrepareApplication();
phoneService = new PhoneApplicationService();
phoneService.Activated += OnActivate;
phoneService.Deactivated += OnDeactivate;
phoneService.Launching += OnLaunch;
phoneService.Closing += OnClose;
Application.ApplicationLifetimeObjects.Add(phoneService);
if (phoneApplicationInitialized) {
return;
}
RootFrame = CreatePhoneApplicationFrame();
RootFrame.Navigated += OnNavigated;
phoneApplicationInitialized = true;
}
You could probably just subclass this and get away with not creating a new PhoneApplicationService but reusing the existing one:
/// <summary>
/// A custom bootstrapper designed to setup phone applications.
/// </summary>
public class CustomPhoneBootstrapper : Bootstrapper {
bool phoneApplicationInitialized;
PhoneApplicationService phoneService;
/// <summary>
/// The root frame used for navigation.
/// </summary>
public PhoneApplicationFrame RootFrame { get; private set; }
/// <summary>
/// Provides an opportunity to hook into the application object.
/// </summary>
protected override void PrepareApplication(PhoneApplicationService phoneAppService, PhoneApplicationFrame rootFrame) {
base.PrepareApplication();
phoneService = phoneAppService;
phoneService.Activated += OnActivate;
phoneService.Deactivated += OnDeactivate;
phoneService.Launching += OnLaunch;
phoneService.Closing += OnClose;
Application.ApplicationLifetimeObjects.Add(phoneService);
if (phoneApplicationInitialized) {
return;
}
RootFrame = rootFrame;
RootFrame.Navigated += OnNavigated;
phoneApplicationInitialized = true;
}
void OnNavigated(object sender, NavigationEventArgs e) {
if (Application.RootVisual != RootFrame) {
Application.RootVisual = RootFrame;
}
}
/// <summary>
/// Occurs when a fresh instance of the application is launching.
/// </summary>
protected virtual void OnLaunch(object sender, LaunchingEventArgs e) { }
/// <summary>
/// Occurs when a previously tombstoned or paused application is resurrected/resumed.
/// </summary>
protected virtual void OnActivate(object sender, ActivatedEventArgs e) { }
/// <summary>
/// Occurs when the application is being tombstoned or paused.
/// </summary>
protected virtual void OnDeactivate(object sender, DeactivatedEventArgs e) { }
/// <summary>
/// Occurs when the application is closing.
/// </summary>
protected virtual void OnClose(object sender, ClosingEventArgs e) { }
}
Disclaimer: Not sure if any of this will work since I've never devved for Windows Phone but as far as I can see that should do what the original bootstrapper did but skip the creation of the application and the root frame. You just need to supply the app and the root frame (or maybe just the app if you can get the root frame from the application object - like I said, no idea what's possible)
I am following the road not taken tutorial, but I get a No suitable mthod found to override error. Here is my XNA project:
Code
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteBatch mSpriteBatch;
Texture2D mTrack;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before
/// starting to run. This is where it can query for any required
/// services and load any non-graphic related content.
/// Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
//Change the resolution to 800x600
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 600;
graphics.ApplyChanges();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
//Create the SpriteBatch used for drawing the Sprites
mSpriteBatch = new SpriteBatch(graphics.GraphicsDevice);
//Load the images from computer into the Texture2D objects
mTrack = Content.Load<Texture2D>("Track");
}
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
mSpriteBatch.Begin();
mSpriteBatch.Draw(mTrack,
new Rectangle(0, 0, mTrack.Width, mTrack.Height),
Color.White);
mSpriteBatch.End();
base.Draw(gameTime);
}
}
}
Error
TheRoadNotTaken.Game1.LoadGraphicsContent(bool)': no suitable method found to override
How can I fix this error?
I believe LoadGraphicsContent has been deprecated. You need to override LoadContent() instead of LoadGraphicsContent(bool loadAllContent)
If that tutorial is for 4.0, then its using outdated information. Here's the MSDN for it: http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.loadgraphicscontent(v=xnagamestudio.31).aspx
I have a problem with Sterling Database for Windows Phone. I implemented the database step by step in my wp7app, but it doesn't serialize my data when new entities are saved. For example: I serialize credentials using sterling database:
var userCredentials = new UserCredentials(userName, password);
App.Database.Save(userCredentials);
App.Database.Flush();
But when the application is reactivated (or re-launched) Sterling doesn't return any values from isolated storage:
var firstOrDefault = App.Database.Query<UserCredentials, string>()
.ToList()
.FirstOrDefault();
My ActivateEngine method looks are standard and TableDefinition is:
CreateTableDefinition< UserCredentials, string >(t => t.UserName),
Why is sterling database doesn't serialize my data? Everything seems to be implemented fine. Please help.
Are you activating and registering the database on startup and diposing on completion as described in the Quickstart?
My personal preference is to use an application service similar to the following:
namespace MyApp.Data
{
using System.Windows;
using Wintellect.Sterling;
using Wintellect.Sterling.IsolatedStorage;
///
/// Defines a an application service that supports the Sterling database.
///
public class SterlingDatabaseService : IApplicationService, IApplicationLifetimeAware
{
public static SterlingDatabaseService Current { get; private set; }
public ISterlingDatabaseInstance Database { get; private set; }
private SterlingEngine _engine;
///
/// Called by an application in order to initialize the application extension service.
///
/// Provides information about the application state.
public void StartService(ApplicationServiceContext context)
{
Current = this;
_engine = new SterlingEngine();
}
///
/// Called by an application in order to stop the application extension service.
///
public void StopService()
{
_engine = null;
}
///
/// Called by an application immediately before the event occurs.
///
public void Starting()
{
_engine.Activate();
Database = _engine
.SterlingDatabase
.RegisterDatabase(new IsolatedStorageDriver());
}
///
/// Called by an application immediately after the event occurs.
///
public void Started()
{
return;
}
///
/// Called by an application immediately before the event occurs.
///
public void Exiting()
{
_engine.Dispose();
}
///
/// Called by an application immediately after the event occurs.
///
public void Exited()
{
return;
}
}
}
If you use this approach, don't forget to add an instance in App.xaml:
<Application.ApplicationLifetimeObjects>
<!-- Required object that handles lifetime events for the application. -->
<shell:PhoneApplicationService Activated="Application_Activated"
Closing="Application_Closing"
Deactivated="Application_Deactivated"
Launching="Application_Launching" />
<data:SterlingDatabaseService />
</Application.ApplicationLifetimeObjects>