Modern UI for WPF- navigation - c#

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;
}
}

Related

Selenium Page Object Model: Best way to handle Return Pages for Modals

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.

Receive a parameter from a page navigation

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
}

Xamarin.Forms: Use of GetMainPage()

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).

System.NullReferenceException creating viewModel

So, I'm trying to find the Umbraco node (as iPublishedContent), and pass it to the viewModel (as ะจ've hijacked a route). So i put this in my controller:
private AddCouponCodesViewModel viewModel;
public AddCouponCodesController(){
//Get iPublished content
IPublishedContent content = Umbraco.TypedContent(1225);
//Pass to viewModel
viewModel = new AddCouponCodesViewModel(content);
RouteData.DataTokens["umbraco"] = content;
}
public ActionResult Index()
{
//return view etc
}
But I'm getting
Exception Details: System.NullReferenceException:
Object reference not set to an instance of an object.
here:
Source Error(AddCouponCodesViewModel.cs):
Line 20:
Line 21: }
Line 22: public AddCouponCodesViewModel(IPublishedContent content)
Line 23: : base(content)
Line 24: {
AddCouponCodeRenderModel.cs:
public class AddCouponCodesViewModel : RenderModel
{
public string test { get; set; }
public List<string> tables { get; set; }
public List<string> errors { get; set; }
public AddCouponCodesViewModel(IPublishedContent content, CultureInfo culture) : base(content, culture)
{
}
public AddCouponCodesViewModel(IPublishedContent content)
: base(content)
{
}
And this is the Global.asax
public class Global : UmbracoApplication
{
protected override void OnApplicationStarted(object sender, EventArgs e)
{
base.OnApplicationStarted(sender, e);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//AreaRegistration.RegisterAllAreas();
//WebApiConfig.Register(GlobalConfiguration.Configuration);
//FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//RouteConfig.RegisterRoutes(RouteTable.Routes);
base.OnApplicationStarting(sender, e);
RouteTable.Routes.MapRoute(
"AddCouponCodes", // Route name
"Admin/{controller}/{action}/{id}", // URL with parameters
new { controller = "AddCouponCodes", action = "Index", id = "" } // Parameter defaults
);
}
}
The content is published (I've checked and double checked), and the node ID is correct.
What I'm basically trying to do here, is to get the route example.com/Admin/{controller}/{action}/{parameter}
To be routed, but having problems connecting it with the umbracoNode (And class RenderModel requires a iPublishContent object as a parameter, but I'm in no luck when trying to pass it anything)
Could someone please help me here, been stuck way too many hours on this :-(
To clarify, if you are hijacking a route, it means that you are overriding the way Umbraco passes it's RenderModel to one of it's published pages. You can either do this globally by overriding the main RenderMvcController, or you can override on a DocumentType-specific basis. So for example, if I have a Homepage doc type, I could create:
public HomepageController : RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
// Create your new renderModel here, inheriting
// from RenderModel
return CurrentTemplate(renderModel);
}
}
This would route all calls to the homepage through this one action. For this, you don't need to define any new routes in the route table. And you should override the render model in the action not in the constructor.
Your question is slightly confusing and it's not entirely clear what you are trying to achieve because:
You have defined a route, and
In your constructor you are calling Umbraco.TypedContent(1225) to retrieve a specific published node
So ... if the admin page you are trying to route has itself been published by Umbraco (and it doesn't sound like it has), the just create a new controller with the name of the page's document type and override the render model in the way described above.
However ... if your admin page hasn't been published by Umbraco and you just want the admin page to access node data, then you have a couple of options:
Create a surface controller, inheriting from SurfaceController. This will give you access to the Umbraco context et al; or
Create a standard controller (preferrably in an Area) and inject the ContentCache using something like Autofac
E.g.:
builder.RegisterControllers(typeof (AdminController).Assembly)
.WithParameter("contentCache", UmbracoContext.Current.ContentCache);
Create a standard controller (preferrably in an Area) and access the node using Umbraco's ContentService API, i.e. new Umbraco.Core.Services.ContentService().GetById(1225)
The difference between the last two approaches is that:
Injecting the ContentCache provides you readonly but very quick access to the published content.
Accessing the ContentService provides you read/write access to the nodes themselves but at the expense of speed as you are querying the database directly.
It depends on what your requirement is.
Either way, it is well worth taking time to read through the documentation for hijacking Umbraco routes, and at least trying to understand what is going on.
Well, I can tell you that your view isn't getting fed anything for the Razor markup because your Index method doesn't feed it anything. That's one problem. I can also tell you, that in your AddCouponCodesViewModel, you'll need an empty constructor, so that the razor syntax can just create an instance, and then populate it to match your submitted object to the view.
Modify your ViewController :
public ActionResult Index()
{
return View(viewModel);
}
Modify your AddCouponCodesViewModel to add an Empty constructor:
public AddCouponCodesViewModel()
{
}
Create a paramaterless constructor on your view model like this:
public AddCouponCodesViewModel():
this(new UmbracoHelper(UmbracoContext.Current).
TypedContent(UmbracoContext.Current.PageId))
{
}
This will get the contexts your other constructors are looking for.
After you've created a class with specific constructors, the compiler stops generating a parameterless one by default. Since you need a parameterless constructor, this is how to get one and still pass in the Umbraco contextual info your viewmodel needs

Make available a variable in multiple forms in compact framework

I'm doing an application for a Windows CE 5.0 device that asks for the username in the first form (when the application is launched), and then I get the userId from a database table.
After that a menu form appears which receives the userId, and I have to send to each constructor of the menu options the userId in order to use it in those forms. I assume there must be a better way to do something like this.
Example:
public partial class Menu : Form
{
int userId;
public Menu(int userId)
{
InitializeComponent();
this.userId = userId;
}
private void buttonDelivery_Click(object sender, EventArgs e)
{
Delivery delivery = new Delivery(userId);
delivery.Show();
this.Hide();
}
...
May be I should use a global variable like this?
public static class UserConfiguration
{
public static int userId;
}
Isn't that also bad practice?
Finally bear in mind that compact framework doesn't support app.config files
Personally I'd vote for "neither", but would instead use some other architectural tools available.
I'd be highly inclined to have a class that incorporates all user info (the ID you're using and then maybe anything else, like name, etc). I'd create an instance and populate that info when the first Form (login) is submitted and I'd keep it in a DI container (I use this one specifically, but any CF-supporting container would work).
I'd then either use injection to either automatically push that instance into any class that needs it, or have the consumer pull it from the container as needed. Which mechanism I use would depend on which container I'm using and exactly how/when I need the info.
Since the data you're after is coming from a database, I'd actually be inclined to use an ORM (I use this one) to pull the data, which would give you the entity instance containing the user info you're after automatically anyway.
in my opinion both ways are good, in some cases some controls do not work properly if you change the constructor signature or in some cases your constructor would not be called if the framework always calls the one with no parameters. But really depends on the specific case.
I like more the method parameters way to pass the values, but the external class with static field would also work fine.
P.S. app.config is not the best place anyway to store runtime specific values so doesn't matter if supported or not by CF in this case ;-)
If you use a controller it can hold all the variables needed. The controller can have a static Instance property that instantiates itself (see Singleton object design pattern). When developing Mobile applications this is very common as memory is often a constraint. The rest of the methods are public members (not static) so you would access like this. You can either make them properties or just use the public member. Even with mobile we tend to not use properties as it just adds unecessary fluff and boxing/unboxing.
In one form you can use:
MainController.Instance.loginID = "me123";
on another you can use
MessageBox.Show("my loginID is: " + MainController.Instance.loginID);
You can also add methods like:
MainController.Instance.ClearSession();
Which internally just sets loginID to null. etc. Personally I use the main controller to show windows as well. Because in mobile we need to make sure our resources are cleaned up as well.
MainController.Instance.ShowLoginForm();
the MainController code as a start should look something like this:
public class MainController : IDisposable {
//all forms we are controlling
LoginForm _loginForm = null;
//all public members
public string loginID = null;
#region Singleton Instance stuff
private static MainController me = null;
private void MainController() { }
public static Instance {
get {
if(me == null) {
me = new MainController();
}
return me;
}
}
#endregion
//all public methods
public void Init(someargshere) {
//TODO some init like load config files, etc.
}
public void Dispose() {
//TODO cleanup
}
public void ClearSession() {
loginID = "";
}
public void ShowLoginForm() {
if(loginForm!=null) {
loginForm.Dispose();
loginForm == null;
}
loginForm = new LoginForm();
loginForm.Show();
loginForm.BringToFront();
}
//etc
}
So the very first thing you do in the Program.cs code is init your main controller
main(string[] args) {
//start a controller
MainController.Instance.Init(passomeargs if needed);
//now fire off our main form
Application.Run(new MainForm());
}
Now all forms there after can access it's data through the MainController
Personally I use commands and have the main controller hide and show forms based on the commands passed in so there is as little logic in the forms as possible. This may or may not lend well to what you are doing.
Good luck

Categories

Resources