Map.SetView() on Windows Phone 8 - c#

Is there a specific time in the page's lifecycle that the Map.SetView() function should be called? In our app we use this on various map objects and it seems to work randomly, sometimes perfectly and sometimes with no effect but also no exception.
example code:
RouteMap.SetView(LocationRectangle.CreateBoundingRectangle(DirectionCoordinates));
Where RouteMap is the mapping component and DirectionCoordinates contains the start/end coordinates for the map.
I can see that the bounding box is being created properly, but the map's positioning is not always being affected even loading the same data. If I add a break point it does seem to work, so I was assuming it had something to do with the map loading, but adding the SetView() functionality to the Loaded event has the same issue. We currently process the map information in the page Loaded event.
Update
I've been testing more and added events to what I could, I know for a fact that the MapLoaded event is being called before SetView. After SetView is called, it is working sometimes and not others. Neither ViewChanging or ViewChanged events are called.

This is obviously not the best solution, but there must be something that is not quite finished loading when the Loaded event is called that is preventing this from finishing.
I added a 100ms sleep to the Map_Loaded event and it has solved the problem I was having.
System.Threading.Thread.Sleep(500);
update
100ms isn't working for some people, you may want to play around with the numbers, 200, 500 etc. It's still a very short delay on the map's load time. I have contacted Microsoft about this and they have told me that they are looking into the issue and we will hopefully have some sort of response from them shortly.
update and edit
Use the following code instead to prevent UI hanging:
await Task.Delay(250);

I tackled this issue using ResolveCompleted event and boolean flag.
private void Map_ResolveCompleted(object sender, MapResolveCompletedEventArgs e)
{
if (this.zoomReq)
{
this.zoomReq = false;
if (this.locationList != null && this.locationList.Count > 0)
{
var viewRect = LocationRectangle.CreateBoundingRectangle(this.locationList);
this.Map.SetView(viewRect);
}
}
}
There is noticeable pause before map zooms but at least this seems to work all the time. The flag is needed because ResolveCompleted is fired every time the map moves.

I was both constructing a map layer (Microsoft.Phone.Maps.Controls.MapLayer) and setting the view (public void SetView(LocationRectangle boundingRectangle);) in an async method:
public async Task CreateMap()
{
map.Add(mapLayer);
map.SetView(locationRectangle);
}
I was doing some loading, that's why I used async.
This would only set the view once, the first time I navigated to the page.
The solution was to dispatch the set view call:
public async Task CreateMap()
{
map.Add(mapLayer);
Dispatcher.BeginInvoke(() =>
{
map.SetView(locationRectangle);
});
}
Hope that helps.

The Loaded event is the proper place for SetView(). You could try creating your rectangle in you OnNavigatedTo method. When I'm working with locations I always start my watcher in OnNavigatedTo and work with any map layers in _Loaded.

I worked myself some time at this problem. It didn't help to put most of the stuff to load into the constructor of the page. I tried to the trick with System.Threading.Thread.Sleep(500) but it took far beyond 500ms to take effect and this wasn't acceptable for me. For some people it helped to trigger an ZoomLevelChanged event and set the view in it. For myself I used a DispatcherTimer in which I used SetView() and fired an `ViewChanging´ event to stop the timer. If you use an animation the difference is pretty small.

I had this problem for MapAnimationKind.Linear but for MapAnimationKind.None it works without any problem
map.SetView(LocationRectangle.CreateBoundingRectangle(...), MapAnimationKind.None);

I had a very similar problem. Basically the setview of map would work the first time a page loaded (i.e. after all the data had finished loading) but if I left the page and came back and did not need to reload all the data, it did not work. While debugging, it seemed like I was setting the information for the map before it was finished loading.
So what I did to resolve the challenge was:
In the XAML - I added an event handler for the Loaded event of the map.
Example: Loaded="myMap_Loaded"
In the myMap_Loaded event, I simply called an async method to wait for the data to load then map
it.
Example:
private void myMap_Loaded(object sender, RoutedEventArgs e)
{
WaitAndLoadMap();
}
Coded the WaitAndLoadMap method to wait for the data to finish loading before loading the
map.
private async void WaitAndLoadMap()
{
//Check if the data is loaded and if it is not - loop.
while (!App.NearbyLocationsViewModel.IsLocationDataLoaded)
await Task.Delay(250);
//Load the map content and set the mapview.
}
It seems to be working. Hope this helps others.

Related

Xamarin Forms - attaching scrollview.Scrolled to a function

When I attach a scrollview to a function like this
textScroll.Scrolled += (sender, e) => { onScrolled(); };
Each time I scroll up or down, OnScrolled() is called multiple times. I know I can get the size of the content and compare it to the ScrollY value, obviously the ScrollY value changes each time, but as far as I can see I won't know when the last call happens (per user scroll).
I only want to call this once per scroll, failing that call it each time as is happening now, but only act when I know I'm on the last call.
Is this possible?
thanks
It is possible, but with a custom renderer for each platform.
On iOS: you will want to implement delegates for DecelerationEnded and WillEndDragging. The reason for also implementing DecelerationEnded is to allow for a fling by the user and waiting for the velocity to come to 0.
On Android it is a bit more complicated. Here is a native Android SO post I followed and translated into c# in a renderer. Works pretty well for me.
Android: Detect when ScrollView stops scrolling
After having both implemented, you can call to your Xamarin.Forms view in order to notify that the view has Stopped scrolling (i.e. final call)

UI function firing before quick event finishes

I have a web browser frame docked in a Silverlight application, and sometimes full-window XAML/Silverlight UI elements can pop up over it. The old problem, which I've more-or-less fixed, is that the web frame's contents didn't seem to play nice with the Silverlight content, always wanting to be rendered on top. This isn't really an issue now since I have an event that fires whenever an application-obscuring popup appears, but now there's a new problem.
My code that launches pop-ups looks like this:
public void OnModuleShown()
{
if (ModuleShown != null)
ModuleShown(this, new ModuleShownEventArgs());
}
public void ShowModule (string uri, string headerTitle, string message, string transition = "DefaultTransition")
{
// Security and destination validation
GetInfoFromURI(uri, out contentKey, out dataKey, out securityToken);
OnModuleShown();
ShowLoadingSpinner();
_loadModule(contentKey, dataKey, securityToken, headerTitle, message, transition);
}
My code that handles the event looks like this:
private void Shell_ModuleShown(object sender, ModuleShownEventArgs e)
{
if (browserFrame.Visibility == Visibility.Visible)
{
browserFrame.Visibility = Visibility.Collapsed;
return;
}
}
The new problem is that even though I call the event before I start loading and displaying the new module, and even though all the event is doing is changing a web frame's visibility, the module tends to appear first if the loading time is short. Since the module's appearance is animated, it looks even worse since the web frame seems to be waiting for the module to finish its animation before it vanishes.
Questions
Is there some kind of threading method I can use to address this? I really don't want to use Thread.Sleep but it's the only one I know of that would fix this without large program changes I can't make. Even better would be if there was a way to get this web frame to play along with Z-indexes or something similar.
I am using Visual Studio 2013, and my project's .NET Framework version is 4.0.

Windows Phone App Freezing while loading main page?

My windows phone app is freezing while loading the main page. I have set breakpoints on the main contructor and it fires the OnNavigatedTo event but never fires the "Loaded" event. It gets through the InitializeComponent() in the contructor. It shows the splash screen and the mainpage application bar, but freezes at that point.
I have recently refactored my main namespace, which was causing the mainpage to not load. I fixed that via the Startup Object.
EVERYTHING was working before the refactoring.
What code should I include? The project is fairly large at this point, so I dont know how much you really want me posting code.
Any ideas??
Ok I found the problem. In App.Xaml.cs I made the following crucial mistake:
Here is my code:
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != null && RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}
And here is what is was supposed to be:
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}
Resharper thought it would be best to check if RootVisual was null first. Incredibly irritating mistake as It would just hang at the startup PNG and never load, obviously because RootVisual wasn't being set.
I've had difficulties like this before. Sounds like it's probably a problem in your XAML but without actually having your solution it's hard to tell.
Here's a couple of things you could try:
Check all your static/dynamic resources to make sure that they're all still resolving correctly.
Check that any bindings and binding converters that you're using are still resolving properly. Converters in particular, I've had issues with them in the past.
If you're getting exceptions, but not tied to a specific point in code:
Try putting breakpoints on any or all custom properties that you might be using. Properties aren't stepped into by the debugger by default. I've found this to be the cause of a number of exceptions in my own work.
Hope this helps!

Skip a page when the back button is pressed, WP7

In the current released version of WP7 you have no programmatic control over the page stack.
I start at A, go to B, and then C. B is the data entry page for new items, so coming back from C I want to logically land at A. Currently I listen for navigation on B that comes from C and force another back onto A. However, the event doesn't happen soon enough to stop the page from displaying on-screen.
A -> B -> C
C -> A
A is the top level list page. B is the new item page. C is the item detail page.
This only occurs on one form so far so my workaround is to override OnNavigatedTo in the page I want to skip, and call "go back" programmatically. However, this has the undesired effect of briefly showing the page and then immediately navigating off of it.
Is there a workable way to stop the flicker?
Should my workaround instead be to take full control of page navigation, including go backs? This will leave the page stack is a strange state, but that would be hidden from the user if I control all navigation.
I know there is a new feature in Mango to pop a page from the page stack programmatically, but I'm curious to know if there is a solution to the problem in the released version.
Another motivation for this is I don't know what version will be best to target, the latest, or the one that is just enough for the app. For the time being I'm sticking with the live version.
You should read this blog post on solving circular navigation issues and you can download this recipe code which demonstrates non-linear navigation.
I have stopped the flickering by making the root frame transparent for the duration of the skip. This example isn't straight from my code.
Firstly on the page you wish to skip, override OnNavigatedTo and test to see where you have come from (this is where my code gets specific, I keep track of where I am):
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// If I've come from page C, go back again.
NavigationService.GoBack();
}
Secondly, in the main App.xaml.cs register an event handler for Navigating (I put it in public App() constructor):
RootFrame.Navigating += RootFrame_Navigating;
Finally, flesh them out to hide the frame and show it again for the duration of the skip:
private bool _skipped;
private void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
RootFrame.Opacity = 100;
RootFrame.Navigated -= RootFrame_Navigated;
}
private void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
{
if (_skipped)
{
_skipped = false;
RootFrame.Navigated += RootFrame_Navigated;
}
if (e.NavigationMode == NavigationMode.Back &&
e.Uri.OriginalString.Contains("ThePage.xaml"))
{
RootFrame.Opacity = 0;
_skipped = true;
}
}
The code to determine when to make the page transparent is also different in my actual code, but I've added an implementation to the answer for illustration. This is almost identical to the code in the recipe linked in another answer, but doesn't involve needing to use a third party DLL.
I provided my own answer because I've seen the sources provided in the other answers before, but never paid attention to the code for hiding the root frame. I am not using the Non-Linear Navigation Service, just the code fragment for frame transparency (I don't need it to detect circular navigation as I'm well aware of the design choices I make in the app and can spot them myself :-)
This suffices as a workaround in the (currently) one case I have where I need to skip a page that doesn't make sense when going back. I'd like to think when Mango comes out I will be best placed targeting the latest version, so this code will soon be defunct.
Sources:
App Hub code recipe for Non-Linear Navigation
Non-Linear Navigation Service Blog Post
There is no way to do it before Mango, except with the flickering way you already know.
In Mango, you can use NavigationService.RemoveBackEntry.
And it's more a question of a bad application design, than anything else. You shouldn't require a immediate page for anything.
Your workaround should be to design a application structure that doesn't require any immediate pages.

Paragraph.BringIntoView() works only when focused

I am working on a text editor that is based on RichEditBox. I have implemented functionality "Go to line" which eventually resolves to
TextPointer.Paragraph.BringIntoView();
Along with this I also set the caret position.
What I found out is that BringIntoView only works when I click on the RichEditBox first (focus it). Otherwise it seems to get ignored. I can see that the caret position has been adjusted by the code around BringIntoView though.
Does anybody know what is the reason/nature of that problem? How can I overcome it?
Found a workaround for this, not sure if it will work in a pure WPF environment, in my case I'm running WPF inside a mainly Windows Forms solution using WPF UserControls where needed.
Instead of invoking BringIntoFocus() immediately, defer it to a later moment by adding it to a queue that gets handled by a timer. For example:
System.Windows.Forms.Timer DeferredActionTimer = new System.Windows.Forms.Timer() { Interval = 200 };
Queue<Action> DeferredActions = new Queue<Action>();
void DeferredActionTimer_Tick(object sender, EventArgs e) {
while(DeferredActions.Count > 0) {
Action act = DeferredActions.Dequeue();
act();
}
}
In your forms constructor, or in the OnLoad event add:
DeferredActionTimer.Tick += new EventHandler(DeferredActionTimer_Tick);
DeferredActionTimer.Enabled = true;
Finally, instead of calling TextPointer.Paragraph.BringIntoView(); directly, call it like this:
DeferredActions.Enqueue(() => TextPointer.Paragraph.BringIntoView());
Note that the Windows Forms timer kicks events off in the main thread (via the message pump loop). If you have to use another timer you need a bit of extra code. I'd recommend you to use System.Timers.Timer rather than the System.Threading.Timer (it's a little more thread-safe). You would also have to wrap the action in a Dispatcher.Invoke structure. In my case, the WinForms timer works like a charm.
Can't you just give the RichTextBox(?) focus first then, using Keyboard.Focus(richTextBox) or richTextBox.Focus()?

Categories