I have a question regarding saving/loading data in Windows Universal Apps 8.1.
I cannot seem to save the data when exiting the app. My call to the serialization is done in the OnSuspending method in App.xaml.cs, but I don't think it is called whenever I close the app.
It sometimes saves the data, sometimes not. Here is my code for OnSuspending and Save methods.
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// TODO: Save application state and stop any background activity
Debug.WriteLine("SUSPENDING");
HabitManager.HabitSerializer.Save();
deferral.Complete();
}
public async static void Save()
{
Debug.WriteLine("SAVED");
var json = JsonConvert.SerializeObject(HabitList.Instance.GetHabits());
var habits = HabitList.Instance.GetHabits();
foreach (var h in habits)
{
Debug.WriteLine("S: " + h);
}
StorageFile saveFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(saveFile, json);
}
So my question is, what could be the problem, and if this isn't a good serializing mechanism, can you suggest me a better one? Thank you
Your OnSuspending call returns before the Save call finishes.
Change Save to return a Task so you can await it:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// TODO: Save application state and stop any background activity
Debug.WriteLine("SUSPENDING");
// Wait fir Save to finish its a sync operations
await HabitManager.HabitSerializer.Save();
deferral.Complete();
}
public async static Task Save()
{
// Same save code
}
Also be careful when testing suspension. The app won't suspend normally while debugging, but VS provides a suspend button to simulate the suspension process.
Related
I need detect rebooting or device starting, to do this I followed this topic (Detecting reboot programmatically in Windows Phone 8.1), but in my case the method canceled is never called in background task.
When I starting debug, my method is called after forcing changes in timezone that made by:
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
My background task is:
public void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral defferal = taskInstance.GetDeferral();
taskInstance.Canceled += TaskInstance_Canceled;
defferal.Complete();
}
private async void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
BackgroundTaskDeferral defferal = sender.GetDeferral();
try
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
await localFolder.CreateFileAsync("bruno.txt", CreationCollisionOption.OpenIfExists);
}
catch (Exception e)
{
Debug.WriteLine("Fail to create File test: " + e);
}
defferal.Complete();
}
I know that is never called, because the next routine is always in false (it works when app is starting MainPage method):
After using some hours in this problem, I found the solution, Windows Phone have a System Trigger type that is called when device is booted, named: SessionConnected.
Thus, only need simple changes:
builder.SetTrigger(new SystemTrigger(SystemTriggerType.SessionConnected, false));
My dashboard needs too much time to get data from database so I have to use async approach to handle this problem here is my code :
public async Task < Stream > LoadDashboard() {
Stream s = new MemoryStream(Encoding.Default.GetBytes(Resource.Dashboard));
s.Position = 0;
return s;
}
private async void frmMaterialDashboard_Load(object sender, EventArgs e) {
Stream dashboardData = await LoadDashboard();
dashboardViewer1.LoadDashboard(dashboardData);
//show UI components for user interact
}
My code doesn't work and I have to wait for data to come from the database. Should I add anything else ?
This part of code takes long time to load data
Stream s = new MemoryStream(Encoding.Default.GetBytes(Resource.Dashboard));
s.Position = 0;
I want to execute this part async. When my form is loaded I want to call LoadDashboard as a background task to get the data from database ,and the main thread show my user interface form .
The component link that I am using :
https://documentation.devexpress.com/#Dashboard/CustomDocument113927
From what it looks like you have no actual async work you can do, you are reading a resource in to a memory stream. Putting the async keyword on somthing does nothing by itself, the function still runs just like it used to. If you want the work to happen in the background you have to tell it to work in the background by using a new thread.
//Get rid of this async stuff here.
public Stream LoadDashboard()
{
Stream s = new MemoryStream(Encoding.Default.GetBytes(Resource.Dashboard));
s.Position = 0;
return s;
}
private async void frmMaterialDashboard_Load(object sender, EventArgs e)
{
//Start LoadDashboad in a background thread and await it.
Stream dashboardData = await Task.Run(() => LoadDashboard());
dashboardViewer1.LoadDashboard(dashboardData);
//show UI components for user interact
}
Another option is to not copy the string to a memory stream and instead get the stream directly
private void frmMaterialDashboard_Load(object sender, EventArgs e)
{
using (var dashboardStream = Resources.ResourceManager.GetStream("Dashboard"))
{
dashboardViewer1.LoadDashboard(dashboardStream);
}
//show UI components for user interact
}
I got rid of the async because DashboadViewer does not provide a way to call LoadDashboard from the background to the best of my knowledge. You will have to wait till it finishes loading or figure out how to get smaller data.
In my WPF app I need to run a quick routine on startup that checks for a new available version. If the version is available, we do the update and then would like to immediately restart the app. Since this is run before the main window appears to the user, it simply appears as though the app took a split second longer to start up.
We're using Squirrel.Windows for our updater. I've made the class below to handle checking for/applying updates.
public class UpdateVersion
{
private readonly UpdateManager _updateManager;
public Action<int> Progress;
public event Action Restart;
public UpdateVersion(string squirrelUrl)
{
_updateManager = new UpdateManager(squirrelUrl);
}
public async Task UpdateVersions()
{
using (_updateManager)
{
UpdateInfo updateInfo = await _updateManager.CheckForUpdate(progress:Progress);
if (updateInfo.CurrentlyInstalledVersion == null)
{
if (updateInfo.FutureReleaseEntry != null)
{
await _updateManager.UpdateApp(Progress);
// Job crashes here
Restart?.Invoke();
}
}
else if (updateInfo.CurrentlyInstalledVersion.Version < updateInfo.FutureReleaseEntry.Version)
{
await _updateManager.UpdateApp(Progress);
// Job crashes here
Restart?.Invoke();
}
}
}
}
Unfortunately Squirrel has made their update process async only, which means the CheckForUpdate and UpdateApp method must use await, making the entire update method asynchronous. I assign the asnyc call to a Task, then simply .Wait() for the update to finish.
The problem comes when I try to restart my app. Based on what I've read, I need to use Dispatcher.Invoke to call the restart due to the fact I am on a non-UI thread when performing the update. However, despite the code below, I still get the same error message:
The Calling thread cannot access this object because a different thread owns it
Any idea how to correctly implement Dispatcher.Invoke in order to restart the app?
// Instantiate new UpdateVersion object passing in the URL
UpdateVersion updateVersion = new UpdateVersion(System.Configuration.ConfigurationManager.AppSettings.Get("SquirrelDirectory"));
// Assign Dispatch.Invoke as Restart action delegate
updateVersion.Restart += () =>
{
Dispatcher.Invoke(() =>
{
Process.Start(ResourceAssembly.Location);
Current.Shutdown();
});
};
// This is here for debugging purposes so I know the update is occurring
updateVersion.Progress += (count) =>
{
Debug.WriteLine($"Progress.. {count}");
};
var task = Task.Run(async () => { await updateVersion.UpdateVersions(); });
task.Wait();
EDIT
Below is a screen shot of the Target attribute of the Restart action. The debugger was paused at the Restar?.Invoke line from above.
Instead of trying to convert asynchronous programming to the old event based pattern, just use it properly. You don't need events to detect when an asynchronous operation finished, nor do you need Invoke to move back to the UI thread. await takes care of both.
You could write code as simple as this:
static readonly SemanticVersion ZeroVersion = new SemanticVersion(0, 0, 0, 0);
private async void Application_Startup(object sender, StartupEventArgs e)
{
await CheckForUpdatesAsync();
}
private async Task CheckForUpdatesAsync()
{
string squirrelUrl = "...";
var updateProgress = new Progress<int>();
IProgress<int> progress = updateProgress;
//Create a splash screen that binds to progress and show it
var splash = new UpdateSplash(updateProgress);
splash.Show();
using (var updateManager = new UpdateManager(squirrelUrl))
{
//IProgress<int>.Report matches Action<i>
var info = await updateManager.CheckForUpdate(progress: progress.Report);
//Get the current and future versions.
//If missing, replace them with version Zero
var currentVersion = info.CurrentlyInstalledVersion?.Version ?? ZeroVersion;
var futureVersion = info.FutureReleaseEntry?.Version ?? ZeroVersion;
//Is there a newer version?
if (currentVersion < futureVersion)
{
await updateManager.UpdateApp(progress.Report);
Restart();
}
}
splash.Hide();
}
private void Restart()
{
Process.Start(ResourceAssembly.Location);
Current.Shutdown();
}
This is just enough code to extract to a separate class:
private async void Application_Startup(object sender, StartupEventArgs e)
{
var updater = new Updater();
await updater.CheckForUpdatesAsync(...);
}
// ...
class Updater
{
static readonly SemanticVersion ZeroVersion = new SemanticVersion(0, 0, 0, 0);
public async Task CheckForUpdatesAsync(string squirrelUrl)
{
var updateProgress = new Progress<int>();
IProgress<int> progress = updateProgress;
//Create a splash screen that binds to progress and show it
var splash = new UpdateSplash(updateProgress);
splash.Show();
using (var updateManager = new UpdateManager(squirrelUrl))
{
var updateInfo = await updateManager.CheckForUpdate(progress: progress.Report);
//Get the current and future versions. If missing, replace them with version Zero
var currentVersion = updateInfo.CurrentlyInstalledVersion?.Version ?? ZeroVersion;
var futureVersion = updateInfo.FutureReleaseEntry?.Version ?? ZeroVersion;
//Is there a newer version?
if (currentVersion < futureVersion)
{
await updateManager.UpdateApp(progress.Report);
Restart();
}
}
splash.Hide();
}
private void Restart()
{
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
}
}
So the actual exception is somewhere in the Restart handler is trying to access the MainWindow get property from another thread based on the stack trace. This is a complete guess, but I would store the original Dispatcher in the OnStartup method and use the stored Dispatcher in the Restart event handler.
Why you are not using SplashScreen ? This SplashScreen would check for new versions, and either download updates, or start the old application.
A lovely tutorial to get you started : EASILY CREATE A WPF SPLASH SCREEN WITH STATUS UPDATES VIA MVVM
I wrote a Windows Phone 8.1 (WINRT) App. I am trying to share an image from my app which is in LocalStorage of the app. I am using Windows Phone 8.1 Share Contract.
private async void OnShareDataRequested(DataTransferManager sender, DataRequestedEventArgs _dataRequestedEventArgs)
{
_dataRequestedEventArgs.Request.GetDeferral();
List<StorageFile> ListObject = new List<StorageFile>();
Uri UriObject = new Uri(FileLocation,UriKind.RelativeOrAbsolute);
_dataRequestedEventArgs.Request.Data.Properties.Title = "Dr. App";
_dataRequestedEventArgs.Request.Data.Properties.Description = "Photo from my Dr. App Album.";
StorageFolder StorageFolderObject;
StorageFile StorageFileObject;
try
{
StorageFolderObject = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync(LocalCache);
StorageFileObject = await StorageFolderObject.GetFileAsync(FileNameSaved);
_dataRequestedEventArgs.Request.Data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromFile(StorageFileObject);
_dataRequestedEventArgs.Request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(StorageFileObject));
ListObject.Add(StorageFileObject);
_dataRequestedEventArgs.Request.Data.SetStorageItems(ListObject);
}
catch(Exception ex_)
{
}
finally
{
_dataRequestedEventArgs.Request.GetDeferral().Complete();
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= OnShareDataRequested;
base.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += OnShareDataRequested;
base.OnNavigatedTo(e);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.ShowShareUI();
}
I am getting PREPARING CONTENT TO SHARE and then it vanishes in a second. ShareUI doesnt open.
The documentation states that the asynchronous work has an upper limit of 200 ms. Are you violating this premise?
DataRequest.GetDeferral(): Use this method when you want to use an asynchronous function call to generate the DataPackage during a share operation. This function must return a DataPackage object within 200ms to prevent the operation from timing out. If your app shares content that takes more time to package, such as a collection of files or photos, don't use this method. Instead, use the SetDataProvider method to assign a delegate to a DataPackage and return that DataPackage to the target app.
Another thing that stands out to me when I look at your code is that you invoke the GetDeferral method twice instead of saving the result from the first invocation.
I want to save the application state when an metro style app receive the suspend event.
I found in this link the code below, but I donĀ“t find in .Net the class ApplicationStateModel:
public App()
{
InitializeComponent();
this.Suspending += new SuspendingEventHandler(App_Suspending);
this.Resuming += new Windows.UI.Xaml.EventHandler(App_Resuming);
}
void App_Resuming(object sender, object e)
{
// Write code to update ui only for items that are outdated.
// This is resume from suspended state, so it does not lose any data
}
async void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
// Write code to store data so that when the application is terminated the state can be recovered.
// Allowed only 5 seconds to do the storage
SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral();
await **ApplicationStateModel**.SaveAllApplicationDataAsync();
await ApplicationStateModel.SaveSessionStateAsync();
deferral.Complete();
}
You can use Windows.Storage.ApplicationData to save local settings.
Take a look at the Sample SDK app for a running app that saves your settings.