Navigation Source in Windows Phone - c#

I have 2 pages in my application, A and B.
If I'm navigation from the outside of the application to A, I want to display a message box. If I'm navigation from B to A, I don't want to display anything.
Is there any way to identify in A the page which initiated navigation? i.e in A.Loaded (or any other event) I need something like
if(pageFromWhichIAmComingFrom == B)
OnNavigatedTo, OnNavigationFrom and OnNavigatedFrom don't seem to help me.

You could use the PhoneApplicationService class to store information about what page you were on last. For example, use OnNavigatedFrom on Page A:
void OnNavigatedFrom(object sender, Eventargs e)
{
PhoneApplicationService.Current.State["LastPage"] = "PageA";
}
And then check for that on the next page:
void OnNavigatedTo(object sender, Eventargs e)
{
if(PhoneApplicationService.Current.State["LastPage"].ToString() == "PageA")
{
// came from page A
}
else
{
// came from a different page
}
}
Hope this helps!
UPDATE:
One more thing I just saw that might be worth trying is using the NavigationService.BackStack property. I haven't tried this, but it seems like it should work. In your OnNavigatedTo event handler, you should be able to get the last entry from the stack to see your last page. This would be simpler and wouldn't require you to set any properties manually. Example:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var lastPage = NavigationService.BackStack.FirstOrDefault();
}
Found here.

Related

How to refresh the one but last ContentPage on the Navigation

Typically, one pops the current page using this from the NavigationStack:
Navigation.PopAsync( true );
How to I use Navigation to redraw the page just before the current page?
Background: The current page changed something that need to get re-presented in the one but last page.
I'm assuming that the data model that you are using is not observable/bindable and thus the page is not "auto-updated"...
You could use MessagingCenter to publish a "Refresh Event" to avoid coupling the two Pages with events...
In your MainPage:
MessagingCenter.Subscribe<MainPage> (this, "RefreshMainPage", (sender) => {
// Call your main page refresh method
});
In your Second Page:
MessagingCenter.Send<MainPage> (this, "RefreshMainPage");
Navigation.PopAsync( true );
https://developer.xamarin.com/guides/xamarin-forms/messaging-center/
As #SushiHangover mentioned, MessagingCenter is a good option.
Another way would be to subscribe to Page2's OnDisappearing() event from page1 and do something to the Page1 UI/data like so:
Edit: The old way I answered this question (see changelog) does work but I have since modified how I do it, after seeing others' examples, to prevent memory leaks. It is better to unsubscribe from the Disappearing event after it has been used. If you plan to use it again then you can just resubscribe to it before running PushAsync() again on your Page2 instance:
private async void OnGoToPage2Clicked(object sender, EventArgs args) {
Page2 page2 = new Page2();
page2.Disappearing += OnPage2Disappearing;
await Navigation.PushAsync(page2);
}
private async void OnPage2Disappearing(object sender, EventArgs eventArgs) {
await _viewModel.RefreshPage1Data(); //Or how ever you need to refresh the data
((Page2)sender).Disappearing -= OnPage2Disappearing; //Unsubscribe from the event to allow the GC to collect the page and prevent memory leaks
}
Here is what I had to do to get hvaughan3's solution to work for me:
private async void OnGoToPage2Clicked(object sender, EventArgs args) {
Page2 page2 = new Page2();
page2.Disappearing += Page2_Disappearing;
await Navigation.PushAsync(page2);
}
private void Page2_Disappearing(object sender, EventArgs e) {
this.Refresh(); // what your refresh or init function is.
}
When I saw that solution I liked that as the option for me since I'm real heavy into using events to solve most of my problems. Thanks hvaughan3!

How to get info about previous page on Frame.GoBack()

Say we have some Page PageA and I have a button that, when clicked, does the following:
Frame.NavigateTo(typeof(PageB));
After the user is done doing stuff, he navigates back from PageB to PageA calling Frame.GoBack()
I want be able to determine that I'm navigating back from PageB
I could use:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
e.NavigationMode
}
But this only tells me that I'm navigating back, not that I'm navigating back from PageB.
Is this even a good windows-phone-guidelines approach (did not find this particular case in the docs)?
I think you should be able to do it by using Frame.ForwardStack property which holds forward navigation history.
A short sample which should work:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var lastPage = Frame.ForwardStack.LastOrDefault();
if (lastPage != null && lastPage.SourcePageType.Equals(typeof(desiredPage)))
{ /* do something */ }
}

How to remove Frame.BackStackDepth?

I am developing a windows 8.1 app which includes several pages. I want to know how can I remove Frame.BackStackDepth so that when user press the back button the app will navigate to the first page.
I tried this but it remove the previous Frame only.
private void backButton_Click(object sender, RoutedEventArgs e)
{
this.Frame.BackStack.RemoveAt(this.Frame.BackStackDepth-1);
this.Frame.GoBack();
}
You can use this extension method:
public static void ResetBackStack(this Frame frame)
{
PageStackEntry mainPage = frame.BackStack.Where(b => b.SourcePageType == typeof(YourPageType)).FirstOrDefault();
frame.BackStack.Clear();
if (mainPage != null)
{
frame.BackStack.Add(mainPage);
}
}
Just override the BackPressed event inside your NavigationHelper class: call that extension method with your frame and then navigate back.
Or just put it inside your EventHandler:
private void backButton_Click(object sender, RoutedEventArgs e)
{
this.Frame.ResetBackStack();
this.Frame.GoBack();
}
As I know from the Windows Phone, you cannot remove backstack with a command or method. it is not such easy. You have to use loops till CanGoBack returns false. This is only way to do this.
I imagined your logic so, remove all pages in backstack except the first opened page. The loop continues up to exception.
Do not forget to define your first page name instead FirstPage.
Also please check the msdn link for more information.
https://social.msdn.microsoft.com/Forums/windowsapps/en-US/3819f389-3bfc-4c59-a919-272927fc9229/navigation-in-a-metro-application?forum=winappswithcsharp
Try this solution.
do
{
this.Frame.BackStack.RemoveAt(this.Frame.BackStackDepth - 1);
} while (Frame.CanGoBack && Frame.BackStack.Last(entry => entry.SourcePageType != typeof(FirstPage)) != null);
this.Frame.GoBack();

At what point in my code did this List<> become empty?

namespace Messages
{
public partial class Email
{
List<Document> attachments = new List<Document>();
protected void Page_Load(object sender, EventArgs e)
{
foreach(Document document in documentList)
{
attachments.Add(document);
}
}
protected void btnSend_Click(object sender, EventArgs e)
{
sendMail(attachments);
}
}
}
As you can guess, I've stripped this code right down for explanation purposes but that's pretty much all I'm doing with it. I've got a feeling it's to do with deep/shallow copying and cloning, if so - can someone help explain what's gone on here and how I can avoid it/populate the list differently.
Thanks a lot,
Dan
EDIT: Sorry, where I've wrote 'documentList' it actually reads:
(List<Document>)Session[Request.QueryString["documentList"]]
So yer - it's coming from a session variable. Using breakpoints I can see the attachments list is being populated just fine, but then when it comes to the click event handler it's empty!? Not null, just count == 0.
It becomes empty because it's not being stored in the ViewState (I'm assuming asp.net webforms here from the method names).
See How to: Save Values in View State and ASP.NET Page Life Cycle Overview
Alternatively store the value in the Session see How to: Save Values in Session State
EDIT2: with the extra info - I've had problems with this before that have been resolved by moving the code out of Page_load and into a helper method (better) and use this in the event callback. I did originally state that the event callback was coming before the Page_Load - however I've just checked this, and it doesn't, however I'm sure that I've had a problem in the past where in certain situations, with child controls, the Page_Load wasn't completing - possibly related to validation.
Anyway it should probably be recoded along the following lines - to remove the dependancy between Page_load and attachments. Using IENumerables (rather than lists) can also be neat - see the final example.
e.g.
List<Document> getAttachments()
{
List<Document> attachments = new List<Document>();
foreach(Document document in (List<Document>)Session[Request.QueryString["documentList"]])
attachments.Add(document);
}
and then in the callback:
protected void btnSend_Click(object sender, EventArgs e)
{
sendMail(getAttachments());
}
however also worth suggesting using LINQ to do it like this:
IEnumerable<Document> getAttachments()
{
return ((List<Document>)Session[Request.QueryString["documentList"]]).Select(doc => doc);
}
protected void btnSend_Click(object sender, EventArgs e)
{
sendMail(getAttachments());
// or if sendMail doesn't accept IEnumerable then do :
//sendMail(getAttachments().ToList());
}

Checking Page.IsPostBack in user controls

Is it recommended to check the Page.IsPostBack in a user control Page_Load Event like
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
}
}
I am getting wierd results
Edit ~ Here is the thing. When the main form is loaded, I use Request.QueryString to get the customer id which I then place in a SESSION variable.
On the control Load event I read the SESSION variable to get the data for that customer. So, do I need to check PostBack at the control level?
Edit ~ Here is the load event of the control
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//Getting and storing the customer account number
if (string.IsNullOrEmpty((string)Session["CustomerNumber"]))
{
Session["CustomerNumber"] = cust.GetCustomerNumber(myHelper.GetCustomerIDFromQueryString());
LoadProductData();
}
}
}
Here is the myHelper Class
static class myHelper
{
public static Guid GetCustomerIDFromQueryString()
{
//Getting the GUID (used as customerid in CRM) from the URL request of the selected account.
return Sql.ToGuid(System.Web.HttpContext.Current.Request["ID"]);
}
}
}
If you use "!IsPostBack" in page load, when the user click other control it do a postBack, so you don't get your data.
I hope that helps you.
Just checking it for no reason? Absolutely not. If you should do something only on first load and not on subsequent post backs then it's the pattern that should be used.
Are you sure that you will always have a "CustomerNumber" already stored in the Session by the time you get to your page? Is there any rhyme or reason that you can find as to when you get data and when you don't?

Categories