I'm calling the Navigate(Uri, Object) method on a Frame, like this:
frame.Navigate(new Uri("..."), myParameter);
How can I access the myParameter object on the page that gets loaded?
I have tried accessing NavigationService in the constructor, however the service is null:
public MyPage()
{
InitializeComponent();
//This is null
NavigationService.Navigated += NavigationService_Navigated;
}
Is there anything I'm missing?
There are couple of overload forms for Frame.Navigate method. Following works perfectly. You can follow the same.
// Source page
frame.Navigate(new MyPage(myParameter));
// Target page
public MyPage()
{
InitializeComponent();
}
public MyPage(TypeOfParameter myParameter) : this()
{
// use myParameter here
}
Related
I'm currently working on the POM of a web app, that allows to open modals from the navigation bar. The navigation bar stays the same for every page you're on. Each modal can be opened from every page.
I have defined a page object for each modal. Also the navigation bar is a pageobject,
What would be the best way to return to the page, that the modal was opened from?
So for example, you are on the Page FooPage and open modal AboutModal. What is the best way to return to FooPage? It should also work for BarPage and other Pages.
My first approach was, that i define a BasePage Object, which only includes the webdriver and navigationbar. i extend every Page on the web app from this BasePage. Then i could do something like this:
Code for FooPage:
public class FooPage: BasePage
{
private NavigationBar NavBar;
public FooPage(IWebDriver driver): base(driver)
{
...
this.NavBar = new NavigationBar(driver);
}
public NavigationBar Navigate()
{
return NavBar;
}
...
}
public class NavigationBar
{
...
public openAboutModal(BasePage currentPage)
{
log.Info("Open About Modal");
Action.Click(NavigationBarElements.AboutButton);
return new AboutModal(Driver, currentPage);
}
}
public class AboutModal
{
...
protected BasePage ReturnPage;
public AboutModal(IWebDriver driver, BasePage returnPage)
{
...
this.ReturnPage = returnPage;
}
public BasePage CloseAboutModal()
{
return this.ReturnPage;
}
...
}
This is not practical and not intuitive, because we have to remember on which pageobject we currently are, when writing tests. Also only the methods from BasePage are available, which means we have to additionaly navigate back to the page we wanted to be on.
So instead of writing
public class ModalTests
{
[Test]
public void CheckAboutModal()
{
Login() // FooPage
.Navigate() //NavigationBar
.openAboutModal() // AboutModal
.doSomeStuff() //AboutModal
.CloseAboutModal(); //FooPage
}
}
we have to do
public void CheckAboutModal()
{
Login() // FooPage
.Navigate() //NavigationBar
.openAboutModal(new FooPage(Driver)) // AboutModal
.doSomeStuff() // AboutModal
.CloseAboutModal() // BasePage
.Navigate() //NavigationBar
.ToFooPage(); // FooPage
}
}
How can I return to the calling Page of the modal, without making Testwriting to complicated?
Rather than write your test as one giant method-chaining call, use variables whenever you need to refer back to a certain page model. Your test can simply become:
var foo = Login();
foo.Navigate()
.openAboutModal()
.doSomeStuff()
.CloseAboutModal();
// Continue your test after closing the modal
foo.SomeOtherOperation();
In cases like this, the modal doesn't need to return anything. The CloseAboutModal() method can be a void return type. Your test should understand the larger context in which the modal is being used, and create local variables appropriately in order to "return" back to the main page.
I have a scenario where I want to call a method when the user of the app navigates to a certain tab of the TabbedPage.
Example: If I navigate to tab no. 3 of my TabbedPage, a certain method shall be called.
How do I achieve that?
By default all tabs of the TabbedPage are loaded when I start the app.
I am writing in Xamarin - C#.
Best regards!
There are two sample ways to achieve that.
One is using OnAppearing method inside the needed item of tab page.
For example, the tab no. 3 of TabbedPage is ItemsPage, then its ItemsPage.xaml.cs code as follows:
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
// Call your needed method here
}
}
The another way is using OnCurrentPageChanged methond inside the tabbedpage.xaml.cs.
For example, the code as follows:
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
}
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
if(CurrentPage.Title == "tab no. 3 title")
{
// call your needed method
}
//Console.WriteLine(CurrentPage.Title);
}
}
I'm currently reading the navigation section from An Introduction to Xamarin.Forms. One should use the GetMainPage() method. But how should that be used?
The default implementation of the app delegate looks like the following:
Applicaton Delegate:
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new App ());
return base.FinishedLaunching (app, options);
}
}
App:
public class App : Application
{
public App ()
{
MainPage = GetMainPage ();
}
public static Page GetMainPage()
{
var mainNav = new NavigationPage(new ListExample());
return mainNav;
}
}
I got it managed to use the GetMainPage()method instead of getting
Application windows are expected to have a root view controller at the end of application launch
If I look into the (old?) examples (example1, example2) the app delegate is different and a CreateViewController() method is available. In my case it is not!
What is the correct way of loading the root page on to the stack?
You don't have to use GetMainPage(); that's just a method you create. The way X.Forms works these days is: it exposes a MainPage property in the Xamarin.Forms Application class. You set this to an instance of a Page. How you create that page is up to you. You can either use
this.MainPage = new ContentPage { Content = ... }
or you create one file per page (which IMHO is best for maintainability):
this.MainPage = new MyLoginPage();
or you use helper methods which create your pages:
this.MainPage = this.GetMainPage();
The main page is the first page of your Forms application. You can set the MainPage property to a different value to show another page.
Earlier versions of Forms used different approaches and not all samples have been updated yet. Now all platforms only need a call to the Forms Init() method and a call to LoadApplication() instead of creating a view controller, an activity or a page (WP8).
I am having some problems with WPFs.
I have a project that has multiple windows, so to control this windows, I have created a controller class. This controller will have a instance of each windows:
this.mainWindow = new MainWindow();
this.loginWindow = new LoginWindow();
this.registerWindow = new RegisterWindow();
The problem comes when I callback from any of the windows to the controller class and from this controller I want to update the information of the window (for example update the value of a property), the information is not being updated
// In controller
public void login(String email, String pass)
{
....
this.loginWindow.showErrorInPassword();
}
// In LoginWindow
public void showErrorInPassword()
{
this.emailErrorImage.Visibility = System.Windows.Visibility.Visible;
}
... but if I send from the LoginWindow a reference of itself to the login function on the controller, the emailErrorImage will be shown
public void login(String email, String pass, LoginWindow lw)
{
....
lw.showErrorInPassword();
}
Seems that the instance that I have in the controller is not the same as the one that is being displayed when I do this.loginWindow.show()
Can someone tell me what am I doing wrong?
You are going to need to bind the UI objects to a MVVM class to update each window.
Use events to call back to the controller.
Here is a brief example. First create a class to contain event args. Doesn't really have to contain anything. It just differentiates between different delegates. Make it its own class in the namespace so everything has access to it.
public class SomeEventArgs: EventArgs
{
}
Inside the window class:
public event EventHandler<SomeEventArgs> CallBackToController;
protected virtual void OnCallBackEvent(object sender, SomeEventArgse)
{
EventHandler<SomeEventArgs> handle = CallBackToController;
if (handle != null)
{
handle(this, e);
}
}
In the controller class, after instantiating the window assign the event to a method.
this.loginWindow = new LoginWindow();
this.loginWindow.CallBackToController += new EventHandler<SomeEventArgs>(MethodToHandleEvent);
Then the Method must have the same form as expected:
private void MethodToHandleEvent(object sender, SomeEventArgs e)
{
// Do something in response.
}
Now anytime you call OnCallBackEvent(this, new SomeEventArgs()) from the window class, the controller class will catch the event and execute MethodToHandleEvent
For instance:
private void LoginWindowBtn_Click(object sender, RoutedEventArgs e)
{
// Logged in ok, let the controller know.
OnCallBackEvent(this, new SomeEventArgs ());
}
There are a ton of tutorials on this, I think this is a better approach to passing references of windows from window to window.
How can I pass parameter beetwen pages? I've tried to add parameters to page uri but it didn't work because I can't use onNavigatedTo event on user control.
Please help
You must use OnFragmentNavigation.
public void OnFragmentNavigation(FragmentNavigationEventArgs e)
{
DoYourStuff(e.Fragment)
}
e.Fragement contains everything past the # in the URI. In example, using
NavigationCommands.GoToPage.Execute("/Pages/CustomerPage.xaml#CustomerID=12345", this);
e.Fragment will be "CustomerID=12345"
It looks like you are coming from a client browser showing web pages world. With WPF you own the app! you can simply set the value on the new page before or after navigating, pass it in with a constructor or access it from a location accessible from both pages. It sounds like the parameter is an argument to the page so I would pass it in with a constructor in this case:
public class APage : Page
{
private object myVar; // use whatever Type you want
public APage
{
InitializeComponent();
}
public APage(object arg) : this()
{
this.myVar = args;
}
}