I have a project which includes many pages. I want to import information to my database periodically whatever the situation of my application is.
I tried to put my code inside App.xaml.cs but it is only saves data for once (I put it inside launching and tried in Constructor. My method is getting device id's location which is like
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
Location loc = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
//Send Data to Database
dclient.CreateUserLocationCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(dclient_CreateUserLocationCompleted);
dclient.CreateUserLocationAsync(1, loc.Latitude, loc.Longitude);
}
and my watcher position changed is inside the constructor.
if (watcher == null)
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
}
MovementThreshold = getSelectedDeviceLocationFrequencyFromInternalFolder();
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
watcher.Start();
and defined globally inside the App.xaml.cs
How can I run this periodically all the time while program runs ? Any other way ? Thanks (To sum up I want to insert the location data periodically to my database.)
You will need to launch a thread when the app launches that sleeps for whatever time it wants to (or wake up on a signal from the app - when the new value is received) and writes the data to your data store inside it.
Related
I've got question. I have application which is a phone book. I would like to create Tile (in Windows Phone main screen) which'll call that number after I click Tile on main screen.
Is that possible? What should I do to make something like that? I can create custom Tile or maybe I should create some method after my application start?
Create the live tile with something like the following code:
string number = "000 - 000 000";
ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(t => t.NavigationUri.ToString().Contains("phone=" + number));
if (tile == null)
{
StandardTileData tileData = new StandardTileData();
tileData.Title = "Call " + number;
ShellTile.Create(new Uri("/MainPage.xaml?phone=" + number, UriKind.Relative), tileData);
}
And then override the OnNavigatedTo in MainPage.xaml, and add the following code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("phone"))
{
string number = NavigationContext.QueryString["phone"];
PhoneCallTask task = new PhoneCallTask();
task.PhoneNumber = number;
task.Show();
}
base.OnNavigatedTo(e);
}
If you have not done it yet, you also need to add the "ID_CAP_PHONEDIALER" capability in the WMAppManifest.xml file, or you will get an exception when calling task.Show(); above.
Now you got a live tile that when clicked will launch the application and call the number (The user must still confirm it in a dialog though, and that is something you can't disable)
Have you tried a flip tile and the using something like this:
http://blog.ecofic.com/?p=406
Write the number to isolated storage then when they click the tile you read the isolated storage and call the number.
You can also use the Mangopollo library from CodePlex to create a secondary live tile: http://mangopollo.codeplex.com/
In my Windows Phone app's main page, users can click a button to do some stuff and that will trigger the live tile to update.
The problem I am having is, if the user clicks the button and then hit the phone's Back button really quickly, the live tile sometimes will not render properly. This issue rarely happens, but it does happen and when it happens it just looks bad...
The way I implement the live tile is, create a user control that looks exactly the same as the live tile and then save it to isolated storage. Then retrieve it and store it in a FliptileData object. Finally I call the Update method on the ShellTile. Please see the following piece of code to demonstrate the process.
// the function that saves the user control to isolated storage
public Uri SaveJpegToIsolatedStorage(FrameworkElement tile, string suffix, int tileWidth = 336, int tileHeight = 336)
{
var bmp = new WriteableBitmap(tileWidth, tileHeight);
// Force the content to layout itself properly
tile.Measure(new Size(tileWidth, tileHeight));
tile.Arrange(new Rect(0, 0, tileWidth, tileHeight));
bmp.Render(tile, null);
bmp.Invalidate();
// Obtain the virtual store for the application
IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(IsolatedStorageFileName + suffix, FileMode.Create, myStore))
{
try
{
bmp.SaveJpeg(fileStream, tileWidth, tileHeight, 0, 100);
}
catch (Exception)
{
return null;
}
}
return new Uri("isostore:/" + IsolatedStorageFileName + suffix, UriKind.Absolute);
}
// save the user control to isolated storage and prepare the FlipTileData object
wideFrontTileImage = SaveJpegToIsolatedStorage((UserControl)this.WideFrontTile, "_wide_front", 691);
var flipTileData = new FlipTileData();
flipTileData.WideBackgroundImage = wideFrontTileImage;
return flipTileData;
// update the live tile
var shellTile = ShellTile.ActiveTiles.FirstOrDefault();
shellTile.Update(customTile.GetFlipTileData(data.UndoneMemosCount == "0" && data.TotalMemosCount == "0"));
I think the reason that's causing all this is, when the user clicks the Back button too quickly, the OS terminates all the processes running within the app and the rendering wasn't done at that time. I'm thinking if there's a way to know when the rendering is finished, so I can cancel the Back button and wait until it's finished then manually exit the app. But I simply dunno how...
Any help on this one will be greatly appreciated!
I have ran into similar issue in my WP8 app. The problem was that I was updating my Tile in ApplicationDeactivated event handler. The thing is you should not update your tiles there, but rather in your MainPage.OnNavigatedFrom override. Once I changed this, it works just fine.
As far as I have understood, if you register a periodic task to deal with your WP7 live tiles, it will not update more than once every half hour. However, I would like to update the data the background agent works on every time the user exits the app.
My scenario is that I have a live tile displaying the first entry in a planner - and depending on what the user does within the app, that planner might get its entries deleted or have a new one up front. To have the live tile present outdated info is not very appealing.
Is this possible - and if so, how to?
I dont know if this is what you are looking for.
My app updates livetiles when the user exits the app. But then I have had problems such as, if the user does not open the app for few days then it does not get updated.
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
ShellTile PrimaryTile = ShellTile.ActiveTiles.First();
StandardTileData tile = new StandardTileData();
if (PrimaryTile != null)
{
tile.BackTitle = resMan.GetString("liveTileTitle");
tile.BackBackgroundImage = new Uri("/Background.png", UriKind.Relative);
if (pCycMan.GetStartDate() == pCycMan.GetDefaultDate())
{
tile.Title = resMan.GetString("liveTileNotTrackingStatus");
}
else
{
tile.Title = App.m_liveTileText;
}
PrimaryTile.Update(tile);
}
}
I think I've narrowed it down to this code in the Deactivation event:
Here's the thing... When I put a break point in this code everything works fine. The application does NOT fail. However, when I take the break point off it fails. What I don't understand is why the try/catch isn't firing.
I should also note that I commented everything out of this event with no break point and the application worked fine. So it is something in this code...
Could it be that the Save Event is not finished for the unsaved object and when it tries to reactivate the Activation event is actually the one failing???
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
//MessageBox.Show("deactivated");
try
{
//if ((Application.Current as App).infoSaved == false)
//{
unsaved unSavedPillInfo = new unsaved();
unSavedPillInfo.RXName = (Application.Current as App).appRXName;
unSavedPillInfo.RXNumber = (Application.Current as App).appRXNumber;
unSavedPillInfo.DosageNotes = (Application.Current as App).appDosageNotes;
unSavedPillInfo.Generic = (Application.Current as App).appGeneric;
unSavedPillInfo.Instructions = (Application.Current as App).appInstructions;
unSavedPillInfo.Reason = (Application.Current as App).appReason;
unSavedPillInfo.Quantity = (Application.Current as App).appQuantity;
unSavedPillInfo.Refills = (Application.Current as App).appRefills;
unSavedPillInfo.Doctor = (Application.Current as App).appDoctor;
unSavedPillInfo.DoctorNumber = (Application.Current as App).appDoctorNumber;
unSavedPillInfo.Pharmacy = (Application.Current as App).appPharmacy;
unSavedPillInfo.PharmacyNumber = (Application.Current as App).appPharmacyNumber;
unSavedPillInfo.OrigDate = (Application.Current as App).appOrigDate;
unSavedPillInfo.ReorderReminder = (Application.Current as App).appReorderReminder;
unSavedPillInfo.ReorderDate = (Application.Current as App).appReorderDate;
unSavedPillInfo.ConsumptionFrequency = (Application.Current as App).appConsumptionFrequency;
unSavedPillInfo.PerscriptionUpdated = (Application.Current as App).perscriptionUpdated;
unSavedPillInfo.PerscriptionUpdated = (Application.Current as App).doctorUpdated;
unSavedPillInfo.PerscriptionUpdated = (Application.Current as App).detailsUpdated;
unSavedPillInfo.PerscriptionUpdated = (Application.Current as App).pharmacyUpdated;
unSavedPillInfo.Save();
//}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
This is hardly perfect but try putting a Messagebox inside of each of the eventhandlers. That way you can tell when each one is firing and see if one isn't firing.
Also you might want to unistall the application often to clear the IsolatedStorage. This is known to create issues if you keep running on the same installation.
EDIT: Yeah from what I have run into, the application can hang if you aren't properly saving to isolated storage. It can also happen if you aren't properly loading data from isolated storage. You might want to try each one seperately. Use a messagebox to make sure it saves and loads properly because VisualStudio will exit the current debugging session.
UPDATE What you should do is create a global variable called unsavedPrescription. Now when the user selects a prescription assign the global variable "unsaved" the prescription they selected. Note: you should not be assigning properties when the app is deactivating because it is very possible it won't save completely which leads to the app hanging. Instead all you have to do is assign the selected prescription to the global variable and change your code in App.xaml.cs to the following:
public unsaved unsavedPrescription {get; set;}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
//Open up IsolatedStorageSettings
IsolatedStorageSettings settings = Isolated StorageSettings.ApplicationSettings;
//Use a model to save prescription
//So create a name/value pair to store the prescription in isolatedstorage
//Notice we used the global variable
settings["UnsavedPrescription"] = unsavedPrescription;
}
private void Application_Activated(object sender, ActivatedEventArgs e)
{
//Now you can easily load the prescription you saved
//I'm reassigning the global variable that will contain the savedprescription
if(settings.TryGetValue("UnsavedPrescription", out prescription)
{
unsavedPrescription = prescription;
}
}
This greatly simplifies your code when loading and saving. Also you'll be able to test using the messageboxes like I said earlier, which isn't professional but it works nicely. Also your not running too much stuff when the app is trying to deactivate. THIS WAY WILL WORK AS I TESTED IT. The way you did it above looks like your running to much code while the app is deactivating which is probably why its hanging. Also this explains why when you remove it, everything works.
I had a problem that seems similar to yours that wasn't actually related to the Saving/Loading from IsolatedStorage itself, but rather the property I was setting / getting had inifinite recursion. This caused the application to terminate before getting to Catch statements.
It might be worthwhile for you to turn off the Debugger stepping over Properties:
Tools -> Options -> Debugger -> Step over Properties and Operators (Managed Only)
public Dictionary<string, object> Dictionary
{
get
{
if (_dictionary == null)
_dictionary = new Dictionary<string, object>();
return _dictionary;
}
set
{
Dictionary = value;
}
}
App Never launches successfully again after Tombstoning. No exception thrown
When data is entered, it ultimately needs to be saved remotely on a server. I do want the app to work if there is no data connection at the time also, so I need to save everything locally on the phone too. The app can then sync with the server when it gets a connection.
This brings up a little issue. I'm used to saving everything on the server and then getting the records back with id's generated from the server for them. If there is no connection, the app will save locally to the phone but not the server. When syncing with the server, I don't see a way for the phone to know when a record comes back which locally record it's associated with. There isn't enough unique data to figure this out.
What is the best way to handle this?
One way I've been thinking is to change the id of the records to a GUID and let the phone set the id. This way, all records will have an id locally, and when saving to the server, it should still be a unique id.
I'd like to know what other people have been doing, and what works and what doesn't from experience.
This is how we done with a first windows phone 7 app finished few days ago with my friend.
It might not be the best solution but 'till additional refactoring it works just fine.
It's an application for a web app like a mint.com called slamarica.
If we have feature like save transaction, we first check if we have connection to internet.
// Check if application is in online or in offline mode
if (NetworkDetector.IsOnline)
{
// Save through REST API
_transactionBl.AddTransaction(_currentTransaction);
}
else
{
// Save to phone database
SaveTransactionToPhone(_currentTransaction);
}
If transaction is successfully saved via REST, it responses with transaction object and than we save it to local database. If REST save failed we save data to local database.
private void OnTransactionSaveCompleted(bool isSuccessful, string message, Transaction savedTransaction)
{
MessageBox.Show(message);
if(isSuccessful)
{
// save new transaction to local database
DatabaseBl.Save(savedTransaction);
// save to observable collection Transactions in MainViewModel
App.ViewModel.Transactions.Add(App.ViewModel.TransactionToTransactionViewModel(savedTransaction));
App.ViewModel.SortTransactionList();
// Go back to Transaction List
NavigationService.GoBack();
}
else
{
// if REST is failed save unsent transaction to Phone database
SaveTransactionToPhone(_currentTransaction);
// save to observable collection Transactions in MainViewModel
App.ViewModel.Transactions.Add(App.ViewModel.TransactionToTransactionViewModel(_currentTransaction));
App.ViewModel.SortTransactionList();
}
}
Every Transaction object has IsInSync property. It is set to false by default until we got confirmation from REST API that it's saved successful on the server.
User has ability to refresh transactions. User can click on a button Refresh to fetch new data from the server. We do the syncing in the background like this:
private void RefreshTransactions(object sender, RoutedEventArgs e)
{
if (NetworkDetector.IsOnline)
{
var notSyncTransactions = DatabaseBl.GetData<Transaction>().Where(x => x.IsInSync == false).ToList();
if(notSyncTransactions.Count > 0)
{
// we must Sync all transactions
_isAllInSync = true;
_transactionSyncCount = notSyncTransactions.Count;
_transactionBl.AddTransactionCompleted += OnSyncTransactionCompleted;
if (_progress == null)
{
_progress = new ProgressIndicator();
}
foreach (var notSyncTransaction in notSyncTransactions)
{
_transactionBl.AddTransaction(notSyncTransaction);
}
_progress.Show();
}
else
{
// just refresh transactions
DoTransactionRefresh();
}
}
else
{
MessageBox.Show(ApplicationStrings.NETWORK_OFFLINE);
}
}
private void DoTransactionRefresh()
{
if (_progress == null)
{
_progress = new ProgressIndicator();
}
// after all data is sent do full reload
App.ViewModel.LoadMore = true;
App.ViewModel.ShowButton = false;
ApplicationBl<Transaction>.GetDataLoadingCompleted += OnTransactionsRefreshCompleted;
ApplicationBl<Transaction>.GetData(0, 10);
_progress.Show();
}
OnTransactionRefreshCompleted we delete all transaction data in local database and get the latest 10 transactions. We don't need all the data, and this way user have synced data. He can always load more data by taping load more at the end of transaction list. It's something similar like those twitter apps.
private void OnTransactionsRefreshCompleted(object entities)
{
if (entities is IList<Transaction>)
{
// save transactions
var transactions = (IList<Transaction>)entities;
DatabaseBl.TruncateTable<Transaction>();
DatabaseBl.Save(transactions);
((MainViewModel) DataContext).Transactions.Clear();
//reset offset
_offset = 1;
//update list with new transactions
App.ViewModel.LoadDataForTransactions(transactions);
App.ViewModel.LoadMore = false;
App.ViewModel.ShowButton = true;
}
if (entities == null)
{
App.ViewModel.ShowButton = false;
App.ViewModel.LoadMore = false;
}
// hide progress
_progress.Hide();
// remove event handler
ApplicationBl<Transaction>.GetDataLoadingCompleted -= OnTransactionsRefreshCompleted;
}
Caveat - I haven't tried this with windows phone development but use of GUID identities is something I usually do when faced with similar situations - eg creating records when I only have a one-way connection to the database - such as via a message bus or queue.
It works fine, albeit with a minor penalty in record sizes, and can also cause less performant indexes. I suggest you just give it a shot.