Windows phone - How to exit on double tap? - windows-phone-7

I'm learning to develop windows phone application. I started with a browser based app by following this tutorial - http://blogs.msdn.com/b/jaimer/archive/2011/02/04/back-button-press-when-using-webbrowser-control-in-wp7.aspx. I'm experimenting with http://m.facebook.com I can correctly use back button to go to the previous page and all that stuff but I'm not able to implement exit on double tap of back button.
I have seen many browsers app which exit after double tapping the back button. for example - Flipkart - http://www.windowsphone.com/en-us/store/app/flipkart/84fc03ea-210d-4e3e-88e0-de502a2434c5
There is no double tab event for back button. How can we achieve this?

You can create a global long that represents the last time the user pressed the back button.
Every time the back button is pressed, you can make your program subtract the number of elapsed ticks. If it has passed a short amount of ticks, you can make your program exit. If not, set the last tick variable once more.
You can get the current tick that represents the current time with System.DateTime.Ticks.
Simple code sample:
long LastExitAttemptTick = DateTime.Ticks;
private void BackButtonPressHandler(...)
{
long thisTick = DateTime.Ticks;
if (LastExitAttemptTick - thisTick < [specified amount])
throw new Exception("Exit Exception"); //You can use XNA, but this is a quick and dirty way of exiting
else
LastExitAttemptTick = DateTime.Ticks;
}
You can use a value of 10,000,000 ticks (1 second). MSDN says 10,000 ticks per millisecond, so 10,000 * 1000 = 10,000,000.
EDIT: Or as you said, you can also use DateTime.Now and use the seconds value instead. Either way works.

well this kind of logic could work for you
make a global variable
int Count=0
protected ovverride void OnBackKeyPress(CancelEventArgs e)
{
if(Count==0)
{
e.Canel=true;
Count++;
}
else if(Count==1)
{
Count=0;
//code for exiting
//may be App.Current.Terminate(); in wp8
//or in wp7
//if (NavigationService.CanGoBack)
//{
// while (NavigationService.RemoveBackEntry() != null)
// {
// NavigationService.RemoveBackEntry();
// }
//}
}
}
Hope this helps

To close the application on double click, you can use DispatcherTimer task to check whether a two clicks are within one second, if yes close the application else start timer and again check. The snippet for that as follows:
make a DispatcherTimer object as a class field like,
DispatcherTimer dt = new DispatcherTimer();
In your class's constructor specify the interval you want to check for double tap and also add event handler to perform some action when specified time has elapsed. You can do in a class's constructor,
dt.Interval = TimeSpan.FromSeconds(1.0);
dt.Tick += delegate(object s, EventArgs e)
{
dt.Stop();
};
Here what we're doing is we're specifying timespan of 1 second to check whether double tap occurs within that second. Tick event is for what we want to do when timer completes its 1 second. We're simply going to stop the timer.
Now navigate to back key event handler and here is my code to check double tap:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
if (!dt.IsEnabled)
dt.Start();
else
new Microsoft.Xna.Framework.Game().Exit();
}
When for the first tap, timer is not started, it will go to if condition and will start the timer. If second tap occurs after 1 second, then the Tick event we wrote in constructor will fire and according to logic written there, the timer will stop.
Now assume the double tap occurs consequently within 1 second. For the 1st tap as usual it will start the timer, if immediately user presses back button again, then in its handler, it will check whether timer is running. As timer has not completed its 1 second interval, else condition will fired up and the application will close.
I used XNA library / shortcut to force close the application. To work with new Microsoft.Xna.Framework.Game().Exit(); method you should add a microsoft.xna.framework.game.dll in a reference.
Make TimeSpan interval as required.
Hope this helps. Thanks.
EDIT:
Sometimes XNA is not installed on windows 8. Here is a solution for that, so that you add above mentioned assembly reference in you project.
http://blogs.msdn.com/b/astebner/archive/2012/02/29/10274694.aspx
You have to download update which is around around 23MB.
To save time here's a Dropbox link to above assembly reference:
https://db.tt/RYTwv7cS

Yes there is no Double Tap event for back button. You have to write your own logic to exit application on Double Tap on device back key tap twice. Here is the solution this may be help you.
Create a Global variable and initialize with zero
Int TapCount =0;
Now Override OnBackKeyPress event with your own logic.
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
TapCount++;
if(TapCount==2)
{
if( windows phone 8 )
{
Application.Current.Terminate();
}
else
{
if (NavigationService.CanGoBack)
{
while (NavigationService.RemoveBackEntry() != null)
{
NavigationService.RemoveBackEntry();
}
}
}
}
else
e.Canel=true;
base.OnBackKeyPress(e);
}

It's very simple. I've implemented it like this:
First declare global variable:
int count;
Now initialize its value in OnNavigatedTo(NavigationEventArgs e) method:
count = 0;
Now at last add the below code to your cs file:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
count++;
e.Cancel = true;
if(count == 2)
{ e.Cancel = false; base.OnBackKeyPress(e); }
}

Related

Xamarin forms switch events lead to infinte loop - Group switches

I am trying to develop an app using Xamarin.Forms. At a certain point I am trying to have multiple switches that are grouped. This is to say that when one switch is toggled, every other switch needs to be untoggled and, at the same time, there needs to be at least one switch always toggled. This is to say that tapping on a switch that is already toggled should not change anything.T
Now my problem is that Xamarin.forms Toggled event for switches can be fired from the UI, but is also fired programmatically. I thought I had found a way around this problem, but still by doing:
-If the switch was turned on, turn off all others and do application stuff.
-else if a switch was turned off, check if there are any others that are on. If not, turn the switch back on. If yes, do nothing.
A sample code for two switches could be:
private void OnFirstToggled(object sender, EventArgs args)
{
if(FirstSwitch.isToggled)
{
//Application stuff.
SecondSwitch.isToggled = false;
}
else if (!SecondSwitch.isToggled)
{
FirstSwitch.isToggled = true;
}
}
private void OnSecondToggled(object sender, EventArgs args)
{
if(SecondSwitch.isToggled)
{
//Application stuff.
FirstSwitch.isToggled = false;
}
else if (!FirstSwitch.isToggled)
{
SecondSwitch.isToggled = true;
}
}
This solution results in an infinite loop when an already toggled switch is tapped. In fact, the isToggled property of the switch alternates between true and false infinitely. However when debugging the other event never seems to be fired (or at least my debugger does not see it). This is why I don't understand where the isToggled property is changed after that first tap.
I know this is probably a very simple issue, but I cannot seem to find the solution somewhere online. Can anyone see the problem or recommend a better, common way to implement this?
I write a simple solution to you to always keep one Switch open from a Switch group.
Let's first add three switch for test, make sure these Switch will fire the same event of Toggled:
<StackLayout>
<!-- Place new controls here -->
<Switch Toggled="Switch_Toggled" x:Name="FirstSwitch"/>
<Switch Toggled="Switch_Toggled" x:Name="SecondSwitch"/>
<Switch Toggled="Switch_Toggled" x:Name="ThirdSwitch"/>
</StackLayout>
In the code behind, I add those Switches into a list, and loop them in Switch_Toggled event to open/close the Switches:
public partial class MainPage : ContentPage
{
List<Switch> switchList;// To store all your Switches
bool isLooping; //To make sure the Switch_Toggled metod not fired a second time during one toogle event
public MainPage()
{
InitializeComponent();
switchList = new List<Switch>();
switchList.Add(FirstSwitch);
switchList.Add(SecondSwitch);
switchList.Add(ThirdSwitch);
isLooping = false;
}
private void Switch_Toggled(object sender, ToggledEventArgs e)
{
//To make sure the Switch_Toggled metod not fired a second time during one toogle event
if (isLooping == true)
{
return;
}
isLooping = true;
Switch clickSwitch = sender as Switch;
clickSwitch.IsToggled = true;
foreach (var tempSwitch in switchList)
{
if (tempSwitch != clickSwitch)
{
if (tempSwitch.IsToggled == true)
{
tempSwitch.IsToggled = false;
}
}
}
isLooping = false;
}
}
You can try this solution and feel free to ask me any question if you don't understand.
Your problem are the two else blocks. Take in account that you're toggling it on anyway.

Xamarin Forms Map Viewable Area event handler

I have a Xamarin form map on my screen and I'm using PropertyChanged event to retrieve geolocation information from my server and display the proper pins on screen.
While coding the solution I noticed the PropertyChanged event is triggered multiple times (up to 10 times) with a single zoom or drag action on the map. This causes unnecessary calls to server which I want to avoid.
Ideally I want to make only one call to server when the final PropertyChanged event is called but I cant's find an easy solution to implement this.
At this point I've added a refresh button to my page that becomes enabled when a PropertyChanged event happens and I disable it after user uses the button.
Obviously this fixed the too many calls to server but made the solution manual.
I was wondering if there is a more elegant way to make the server call but do it automatically.
Thanks in advance.
I just test the PropertyChanged event on iOS side and it just triggered one time with a single zoom or drag action on the map.
While if it really triggered multiple times, you can use a timer to call the server when the final PropertyChanged event is called, for example:
public partial class MapPage : ContentPage
{
Timer aTimer;
public MapPage()
{
InitializeComponent();
customMap.PropertyChanged += CustomMap_PropertyChanged;
}
private void CustomMap_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (aTimer != null)
{
aTimer.Enabled = false;
aTimer.Stop();
aTimer.Close();
}
aTimer = new Timer();
aTimer.Interval = 1000;
aTimer.Enabled = true;
aTimer.Elapsed += ATimer_Elapsed;
aTimer.Start();
}
private void ATimer_Elapsed(object sender, ElapsedEventArgs e)
{
aTimer.Stop();
//do web request
Console.WriteLine(sender);
Console.WriteLine("CustomMap_PropertyChanged");
}
}
In the above code, I set the Interval = 1 second, that means in 1 second, whatever how many times PropertyChanged triggered, only the last call will trigger the ATimer_Elapsed function.
The Interval can be set to any value depending on your requirement.

Windows Forms Event before the Closing event? [duplicate]

I have a form that spawns a BackgroundWorker, that should update form's own textbox (on main thread), hence Invoke((Action) (...)); call.
If in HandleClosingEvent I just do bgWorker.CancelAsync() then I get ObjectDisposedException on Invoke(...) call, understandably. But if I sit in HandleClosingEvent and wait for bgWorker to be done, than .Invoke(...) never returns, also understandably.
Any ideas how do I close this app without getting the exception, or the deadlock?
Following are 3 relevant methods of the simple Form1 class:
public Form1() {
InitializeComponent();
Closing += HandleClosingEvent;
this.bgWorker.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text = Environment.TickCount.ToString(); }));
}
}
private void HandleClosingEvent(object sender, CancelEventArgs e) {
this.bgWorker.CancelAsync();
/////// while (this.bgWorker.CancellationPending) {} // deadlock
}
The only deadlock-safe and exception-safe way to do this that I know is to actually cancel the FormClosing event. Set e.Cancel = true if the BGW is still running and set a flag to indicate that the user requested a close. Then check that flag in the BGW's RunWorkerCompleted event handler and call Close() if it is set.
private bool closePending;
protected override void OnFormClosing(FormClosingEventArgs e) {
if (backgroundWorker1.IsBusy) {
closePending = true;
backgroundWorker1.CancelAsync();
e.Cancel = true;
this.Enabled = false; // or this.Hide()
return;
}
base.OnFormClosing(e);
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (closePending) this.Close();
closePending = false;
// etc...
}
I've found another way. If you have more backgroundWorkers you can make:
List<Thread> bgWorkersThreads = new List<Thread>();
and in every backgroundWorker's DoWork method make:
bgWorkesThreads.Add(Thread.CurrentThread);
Arter that you can use:
foreach (Thread thread in this.bgWorkersThreads)
{
thread.Abort();
}
I used this in Word Add-in in Control, which i use in CustomTaskPane. If someone close the document or application earlier then all my backgroundWorkes finishes their work, it raises some COM Exception(I don't remember exatly which).CancelAsync() doesn't work.
But with this, I can close all threads which are used by backgroundworkers Immediately in DocumentBeforeClose event and my problem is solved.
Here was my solution (Sorry it's in VB.Net).
When I run the FormClosing event I run BackgroundWorker1.CancelAsync() to set the CancellationPending value to True. Unfortunately, the program never really gets a chance to check the value CancellationPending value to set e.Cancel to true (which as far as I can tell, can only be done in BackgroundWorker1_DoWork).
I didn't remove that line, although it doesn't really seem to make a difference.
I added a line that would set my global variable, bClosingForm, to True. Then I added a line of code in my BackgroundWorker_WorkCompleted to check both e.Cancelled as well as the global variable, bClosingForm, before performing any ending steps.
Using this template, you should be able to close your form out at any time even if the backgroundworker is in the middle of something (which might not be good, but it's bound to happen so it might as well be dealt with). I'm not sure if it's necessary, but you could dispose the Background worker entirely in the Form_Closed event after this all takes place.
Private bClosingForm As Boolean = False
Private Sub SomeFormName_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
bClosingForm = True
BackgroundWorker1.CancelAsync()
End Sub
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Run background tasks:
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Else
'Background work here
End If
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Not bClosingForm Then
If Not e.Cancelled Then
'Completion Work here
End If
End If
End Sub
Can you not wait on the signal in the destructor of the form?
AutoResetEvent workerDone = new AutoResetEvent();
private void HandleClosingEvent(object sender, CancelEventArgs e)
{
this.bgWorker.CancelAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text =
Environment.TickCount.ToString(); }));
}
}
private ~Form1()
{
workerDone.WaitOne();
}
void backgroundWorker1_RunWorkerCompleted( Object sender, RunWorkerCompletedEventArgs e )
{
workerDone.Set();
}
Firstly, the ObjectDisposedException is only one possible pitfall here. Running the OP's code has produced the following InvalidOperationException on a substantial number of occasions:
Invoke or BeginInvoke cannot be called
on a control until the window handle
has been created.
I suppose this could be amended by starting the worker on the 'Loaded' callback rather than the constructor, but this entire ordeal can be avoided altogether if BackgroundWorker's Progress reporting mechanism is used. The following works well:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!this.bgWorker.CancellationPending)
{
this.bgWorker.ReportProgress(Environment.TickCount);
Thread.Sleep(1);
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.textBox1.Text = e.ProgressPercentage.ToString();
}
I kind of hijacked the percentage parameter but one can use the other overload to pass any parameter.
It is interesting to note that removing the above sleep call clogs the UI, consumes high CPU and continually increases the memory use. I guess it has something to do with the message queue of the GUI being overloaded. However, with the sleep call intact, the CPU usage is virtually 0 and the memory usage seems fine, too. To be prudent, perhaps a higher value than 1 ms should be used? An expert opinion here would be appreciated... Update: It appears that as long as the update isn't too frequent, it should be OK: Link
In any case, I can't foresee a scenario where the updating of the GUI has to be in intervals shorter than a couple of milliseconds (at least, in scenarios where a human is watching the GUI), so I think most of the time progress reporting would be the right choice
I really dont see why DoEvents is regarded as such a bad choice in this case if you are using this.enabled = false. I think it would make it quite neat.
protected override void OnFormClosing(FormClosingEventArgs e) {
this.Enabled = false; // or this.Hide()
e.Cancel = true;
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy) {
Application.DoEvents();
}
e.cancel = false;
base.OnFormClosing(e);
}
Your backgroundworker should not use Invoke to update the textbox. It should ask the UI thread nicely to update the textbox using event ProgressChanged with the value to put in the textbox attached.
During event Closed (or maybe event Closing), the UI thread remembers that the form is closed before it cancels the backgroundworker.
Upon receiving the progressChanged the UI thread checks if the form is closed and only if not, it updates the textbox.
This won't work for everyone, but if you are doing something in a BackgroundWorker periodically, like every second or every 10 seconds, (perhaps polling a server) this seems to work well to stop the process in an orderly manner and without error messages (at least so far) and is easy to follow;
public void StopPoll()
{
MyBackgroundWorker.CancelAsync(); //Cancel background worker
AutoResetEvent1.Set(); //Release delay so cancellation occurs soon
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (!MyBackgroundWorker.CancellationPending)
{
//Do some background stuff
MyBackgroundWorker.ReportProgress(0, (object)SomeData);
AutoResetEvent1.WaitOne(10000);
}
}
I'd pass in the SynchronizationContext associated with the textbox to the BackgroundWorker and use that to perform Updates on the UI thread. Using SynchronizationContext.Post, you can check if the control is disposed or disposing.
What about Me.IsHandleCreated?
Private Sub BwDownload_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BwDownload.RunWorkerCompleted
If Me.IsHandleCreated Then
'Form is still open, so proceed
End If
End Sub
Another way:
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
while (backgroundWorker.IsBusy)
{
Application.DoEvents();
}
}
One solution that works, but too complicated. The idea is to spawn the timer that will keep trying to close the form, and form will refuse to close until said bgWorker is dead.
private void HandleClosingEvent(object sender, CancelEventArgs e) {
if (!this.bgWorker.IsBusy) {
// bgWorker is dead, let Closing event proceed.
e.Cancel = false;
return;
}
if (!this.bgWorker.CancellationPending) {
// it is first call to Closing, cancel the bgWorker.
this.bgWorker.CancelAsync();
this.timer1.Enabled = true;
}
// either this is first attempt to close the form, or bgWorker isn't dead.
e.Cancel = true;
}
private void timer1_Tick(object sender, EventArgs e) {
Trace.WriteLine("Trying to close...");
Close();
}

How do I handle double click on a GWT web application running on a touch device?

I have built a GWT (2.5) web application that, among other things, uses a DataGrid. I have used addDomHandler to add a DoubleClickEvent to select a row and perform an action, and it works great on the desktop. However, when I run the web application on a touch device, the double click zooms the screen instead. Is there are proper way to handle that? I would prefer to override the default behavior of zooming, but I have no idea where to begin. I suppose a long press might be more appropriate, but I have no idea where to begin with that either.
The code:
_dataGrid.addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
// Do something exciting here!
}
}, DoubleClickEvent.getType());
The only idea I have it that stopping the propagation of the DOM event may prevent the default zoom behavior.
Although I'd be curious is it registers as a double-click event at all on a touchscreen device. Would be worth trying just putting the handler on Root and seeing if you can even catch the event.
Also try this: just have your application catch -any- DOM event and simply write the name out somehow. That way you, should find out what event is triggering (might be one for long touch!) and can write a handler for that.
OK, I found a solution, but it's probably pretty unique to my situation. What I did was keep the double click handler and I also implemented a slow double click. In other words, if you select the same row of the DataGrid twice in sequence, no matter how fast you do it, then the application interprets that as a double click. Here is the code:
result.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Scheduler.get().scheduleDeferred(new Command() {
public void execute () {
if (result.isDoubleTap()) {
// Do the same thing a a double click.
}
}
});
}});
I had a problem with the click handler being fired before the selection change handler, so I had to defer the processing of the click event with the Scheduler. The "result" is a Composite that contains the DataGrid and some other stuff, and in each "result" I store the last selected item in a private variable. Then in IsDoubleTap() all I do is see if the current selection is the same as the last one:
public boolean isDoubleTap() {
boolean result = false;
String selected = getSelected();
if (_lastSelect != null && selected != null && selected.equals(_lastSelect))
result = true;
_lastSelect = selected;
return result;
}
So effectively if you do a normal double click or a slow double click you get the same action. I'm just glad that while I use this result object many places, it is the only place that I use a double click. And I would REALLY like to have a conversion with the committee that decided overriding standard double click behavior with a touch device was a GOOD thing.

Location service returning previous coordinates on WP7

I'm using the code from the official site, and I am consistently seeing the same behavior on multiple test devices - instead of getting the current location of the device, it gets the previous location (up to 30 miles away, where I was an hour ago).
private void setupMaps()
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
watcher.MovementThreshold = 10.0f;
watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_statusChanged);
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
new Thread(startLocServInBackground).Start();
}
void startLocServInBackground()
{
watcher.TryStart(true, TimeSpan.FromSeconds(60));
}
void watcher_statusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
// The Location Service is disabled or unsupported.
// Check to see if the user has disabled the location service.
if (watcher.Permission == GeoPositionPermission.Denied)
{
// the user has disabled LocServ on their device.
showMessage("Location is required but it is disabled. Turn it on in Settings");
}
else
{
showMessage("Location is not functioning on this phone. Sorry, Crux cannot run");
}
break;
case GeoPositionStatus.Initializing:
// The location service is initializing.
LoadingInfo.Text = "finding location";
break;
case GeoPositionStatus.NoData:
// The Location Service is working, but it cannot get location data
// due to poor signal fidelity (most likely)
// this fired intermittently when data was coming back :/
//MessageBox.Show("Location data is not available.");
break;
case GeoPositionStatus.Ready:
// The location service is working and is receiving location data.
//statusTextBlock.Text = "Location data is available.";
LoadingInfo.Text = "Location found";
// THIS FIRES AFTER POSITION CHANGED HAS STOPPED FIRING
break;
}
}
private void initPostPanel()
{
PostPanel.Visibility = Visibility.Visible;
}
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
// update the textblock readouts.
latitude = e.Position.Location.Latitude.ToString("0.0000000000");
longitude = e.Position.Location.Longitude.ToString("0.0000000000");
// THIS FIRES TWICE, BEFORE STATUS IS FIRED AS READY. THEN THIS DOESN'T CALL AGAIN
}
What I would expect to have happen is to get a continuous series of calls to PositionChanged after StatusChanged is called with a Status of Ready. If the calls continued after Ready, I expect I would eventually get the correct coordinates - but it never calls after that point.
This does not occur with the emulator, only on the actual device (making this extremely difficult to test - since it actually involves driving between each test!)
I am running the source code from the tutorial as well, and it does roughly the same.
Can anyone tell me more about the expected behavior here and how I get what I need - which is simply a set of coordinates for the device at the current location when the app is being used.
I had the same problem myself - there are 2 parts to this.
Turns out the GeoCoordinateWatcher returns the last known good position - which is almost always out of date. What I do is something like this:
Check that the status is GeoPositionStatus.Ready and then ensure the datetime of the position is recent (within the last 5 mins). You can then go further and check that e.Position.Location.HorizontalAccuracy <= 350 (so under 350m) - but doing this with the datetime check as well can cause the phone to take a long time to get a position the lower you set the accuracy, so it may be best to get an initial position with the date check, and then continue to try get a better position with the Accuracy check. I also start the watcher as soon as the app starts to get faster results.
The other issue is MovementThreshold. If you do as I said above, it might still take a while before you get an accurate position, and you will likely experience the intermittent problem you mentioned where it does not fire the 2nd time (it does eventually, depending how long you wait - this can be minutes). The best thing I have found is to start the GeoCoordinateWatcher with the threshold set to 0. Once you have got an accurate position, stop the watcher, set the threshold to the actual one you want (eg 10), and then start it again. If you set this without stopping first, it will not use the new value.
<!-- language: c# -->
GeoPositionStatus = CurrentGeoDeviceStatus;
static void geoCoordWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
CurrentGeoDeviceStatus = e.Status;
}
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (gpsReady && e.Position.Timestamp.DateTime.AddMinutes(5) > DateTime.Now)
{
latitude = e.Position.Location.Latitude.ToString("0.0000000000");
longitude = e.Position.Location.Longitude.ToString("0.0000000000");
locReady = true;
}
}
It looks like the fix was to block it from using the first value and getting it from the second event instead:
bool gpsReady = false;
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (gpsReady)
{
latitude = e.Position.Location.Latitude.ToString("0.0000000000");
longitude = e.Position.Location.Longitude.ToString("0.0000000000");
locReady = true;
}
gpsReady = true;
}
I'm still baffled about why I don't get more events or why it fires an incorrect value first, but the above code seems to be working.
One note, for anyone else trying this, is that you might think getting the value in the StatusChanged event would work, like so:
case GeoPositionStatus.Ready:
latitude = watcher.Position.Location.Latitude.ToString("0.0000000000");
longitude = watcher.Position.Location.Longitude.ToString("0.0000000000");
break;
I don't know why, but the above code seemed to work perfectly when I was running attached to the debugger and then frequently hung (that is, the event never fired and my UI seemed to hang) in practical use. I never managed to reproduced the issue while attached to the debugger.
UPDATE: It looks like this isn't working all of the time. In some cases it doesn't fire the second time and so my code never completes running. If anyone can provide more information around this that gets me closer to simply having the actual current location of the device without fail, I will definitely mark your answer as the answer. Thanks!

Resources