I am trying to make a Diary application in UWP, using Visual Studio Community 2015. So All diary entries are kept in an ObservableCollection (of type DiaryEntry which has three strings). In the OnSuspending method of the App class I serialize (using XmlSerializer) and save the ObservableCollection to the LocalFolder of the app. In the OnLaunched method of the App class I deserialize the data and place it in the ObservableCollection again.
When I run this app in debug mode it is working 100% every time, but in release mode I lose the ObservableCollection once I close and open the app again. Other times it works once or twice but I still lose the data. What I would like to know is how can I get my app working in release mode?
These are the methods I am using:
private async void SaveCollection(string xml)
{
//Serializing our observablecollection and saving it to the local folder
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("Diary.txt", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(sf, xml);
}
private async Task<string> GetSavedCollection()
{
try
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFile sampleFile = await storageFolder.GetFileAsync("Diary.txt");
string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
return text;
}
catch (FileNotFoundException e)
{
return "";
}
}
public static string ToXml(ObservableCollection<DiaryEntry> d)
{
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<DiaryEntry>));
StringBuilder stringBuilder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings()
{
Indent = true,
OmitXmlDeclaration = true,
};
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings))
{
serializer.Serialize(xmlWriter, d);
}
return stringBuilder.ToString();
}
// Deserialize from xml
public static ObservableCollection<DiaryEntry> FromXml(string xml)
{
if (xml == "")
{
return new ObservableCollection<DiaryEntry>();
}
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<DiaryEntry>));
ObservableCollection<DiaryEntry> value;
using (StringReader stringReader = new StringReader(xml))
{
object deserialized = serializer.Deserialize(stringReader);
value = (ObservableCollection<DiaryEntry>)deserialized;
}
return value;
}
This is how I call these methods:
Diaries = FromXml(await GetSavedCollection());
SaveCollection(ToXml(Diaries));
I suspect it's to do with a misunderstanding of the app lifecycle and where you have added your code to the OnLaunched
and OnSuspending
methods.
To understand the lifecycle, please see the docs here and this blog article.
Now those methods.
Let's deal with OnSuspending
first as that's simpler. Your code should look like this:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
SaveCollection(ToXml(Diaries));
deferral.Complete();
}
Note that if you're calling SaveCollection
after deferral.Complete();
it may not finish before the app is terminated and could be the cause of not having
The OnLaunching
method is more complicated. I suspect you have code like this:
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
Diaries = FromXml(await GetSavedCollection());
}
This might seem the logical thing to do based on the TODO comment in the template but won't be right for your scenario where you want to always load from disk when restarting the app. The above wouldn't, for example, load the content when the app previously close by the user. I assume you'd want to load the diary entries then too. (Because you haven't provided full repro steps I can't know exactly how you're testing and recreating the behavior you're seeing.)
Instead, load the Diaries, if need be, just before the 'PrelaunchActivated
check.
if (Diaries == null)
{
Diaries = FromXml(await GetSavedCollection());
}
if (e.PrelaunchActivated == false)
OR
It's an issue with how you've defined and are calling SaveCollection
.
You've defined SaveCollection
as async void
. This is bad. The only things that should be async void
are event handlers. You should have defined it as async Task
and awaited the call to it:
private async Task SaveCollection(string xml)
{
System.Diagnostics.Debug.WriteLine(xml);
//Serializing our observablecollection and saving it to the local folder
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("Diary.txt", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(sf, xml);
}
await SaveCollection(ToXml(Diaries));
Without awaiting this call it may not complete correctly. That this behavior is more obvious under .NetNative compilation would not surprise me.
Or it may be a combination of both issues.