(Sorry if this question is stupid or has already been asked before)
I'm pretty new to .NET MAUI and I'm trying to build an app and I have this code:
private async void BtnOptionsClicked(object sender, EventArgs e)
{
OptionsPage optPage = new();
NavigationPage navPage = new(optPage)
{
Title = optPage.Title
};
navPage.ToolbarItems.Add(new ToolbarItem("BackButtonPlaceholder", "", async () =>
{
System.Diagnostics.Debug.WriteLine("Back button pressed");
await Navigation.PopAsync();
}));
await Navigation.PushAsync(navPage);
}
I'm trying to add a back button for a specific page, not the MainPage
The problem is that the NavigationPage appears the first time and works, but the second time around it just doesn't show up, only the OptionsPage does
Btw it works fine on Android, just not Windows (I can't test on other platforms)
I tried using RemovePage instead of PopAsync and I tried adding the ToolbarItem in the OptionsPage class, nothing I tried works...
You are mixing Shell and NavigationPage, by default a .NET MAUI project is created with a Shell.
NavigationPage is hierarchicall (you push / pop pages on a stack), it's simpler to use and is more flexible but you have manage everything yourself (like menus for exemple).
Shell is URI-based navigation (work the same as a website pages with urls like "/users/1/profile"), it comes with pre designed menu (tabs and flyout) and make it possible to navigate to absolute URL (which is not easy with NavigationPage). It is "replacing" NavigationPage since it cover a lot of use cases and remove a lot of boilerplate code related to navigation (that's why it's the default).
Since Shell is based on NavigationPage using the NavigationPage methods with Shell wont do any error, you should not mix them though (they are not meant to be used together).
If you want to use NavigationPage you should modify ./App.xaml.cs:
public partial class App : Application
{
public App()
{
InitializeComponent();
// Replace :
// MainPage = new AppShell();
// By this :
MainPage = new NavigationPage(new MainPage());
}
}
Now you can use Navigation.PushAsync(page);, the page object should inherit ContentPage and not another NavigationPage.
If you want to keep Shell read this and use await Shell.Current.GoToAsync("../../route"); to navigate between page.
Related
I have a .Net MAUI app that uses Shell TabBar for navigating. Besides navigating to various pages, some tabs should just execute some code without showing any content. As TabBar does not provide such a functionality, I used OnAppearing() method of a dummy empty page, and after my code call
Shell.Current.GoToAsync("..");
to return to the previous page. But for some reason, this does not work, and the empty page is still displayed. If I use some actual page, e.g.
Shell.Current.GoToAsync(nameof("SomePage"));
the navigation works as expected, and SomePage is displayed.
Why doesn't ".." work for me?
Here is the code I use:
public class Page1 : ContentPage
{
protected override void OnAppearing()
{
// Some code I need to execute
Shell.Current.GoToAsync("..");
}
}
In my app I have multiple pages, but when I try and navigate backwards, instead of going to the previous page it always goes to the root page (MainPage). On Android, it's bad because you lose all context. On iOS it's worse, because it causes an exception in the Program class here:
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
The exception is System.NullReferenceException. Message=Object reference not set to an instance of an object.
I'm navigating like this - I'm starting out on my MainPage, and I navigate to the next page like this:
FriendsPage fp = new FriendsPage();
await Navigation.PushAsync(fp, true);
On that page, I'm going to the next page like this:
FriendRatingsPage frp = new FriendRatingsPage() { SearchResults = results, FriendRatingListType = FriendRatingsPage.RatingListType.FriendsItemRatings };
#if ANDROID
await Navigation.PushAsync(frp, true);
#else
await Navigation.PushModalAsync(frp, true);
#endif
Modal pages are broken it seems for Android right now, which is why the two different approaches.
Once I'm on the third page, I want to go back to the previous page. So I follow the directions explained here: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/shell/navigation. It says "Backwards navigation can be performed by specifying ".." as the argument to the GoToAsync method". So I have a click handler that does so like this:
await Shell.Current.GoToAsync("..");
And that's where things go sideways as I described above - going back to the MainPage instead of the previous page, which was the FriendsPage. I've gone in circles on this but clearly am missing something. Can anyone tell me what I'm doing wrong here?
I would like to have a multi-tab/windowed Eclipse RAP application.
I am able to open a second window using
UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
launcher.openURL("/gasf?foo=other_perspective");
Where I use the foo paramter to select the perspetive I want. However using this method will create a speparate http session, thus the various listeners and so on won't communicate with my first window.
I also tried opening a second window/page using
PlatformUI.getWorkbench().getActiveWorkbenchWindow().openPage("other_perspective" , null);
But this merely changes the current window perspective but does not open a second window or tab in my browser.
Has anyone achieved a multi-tab RAP application with working selectionlisteners between the tabs?
Thanks for any help you can provide
EDIT:
THANKS a lot ralfstx, as you pointed out, I can share the listeners or anything using the shared HTTP session, so far so good. Now the next step is to be able to update a tab based on an external event.
To try my idea of refresh from another tab, I did a dummy timer that does something 2 seconds later (i.e. simulate something triggered from another tab) with:
final ServerPushSession pushSession = new ServerPushSession();
pushSession.start();
Display display = Display.getDefault();
NavigationView navigationView = ((NavigationView) window.getActivePage().findView(NavigationView.ID));
timer.schedule(new TimerTask() {
#Override
public void run() {
display.asyncExec(new Runnable() {
public void run() {
navigationView.doSomething();
}
});
}
}, 2000);
This works! The pushSession.start() forces the UI to refresh without any user interaction. So now the action doSomething() is executed on the navigationView as soon as the 2 seconds are reached.
My only remaining concern is how much load this puts on the server, but its a reasonable solution at least. I validated your answer.
EDIT2:
Just to be complete, to make sure not bump in an invalid Thread access error since we are updating a display from another display, in the doSomething() method we must execute actions using display.asyncExec:
Display display = Display.getCurrent();
public void doSomething() {
display.asyncExec(new Runnable() {
public void run() {
treeViewer.refresh();
}
});
}
With the current architecture of RAP, you can't spread workbench windows over different browser tabs. Every new browser starts a new UISession which implies another Display (see Scopes in RAP).
However, the HttpSession should be the same (unless you have cookies turned off), so you could use this as a means of communicating between different browser tabs.
I am trying a simple Android app using Xamarin Forms with C# for the coding. In my Main.axml there's a button, on clicking which I'd like to go to a new page, say XInfo.axml.
Now this is my MainActivity.cs:
using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Xamarin.Forms;
namespace AlberoPizza
{
[Activity(Label = "Albero Pizza", MainLauncher = true, Icon="#drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button btn = FindViewById<Button>(Resource.Id.MyButton);
btn.Clicked += (object sender, EventArgs e) => {
};
}
}
}
Now, I am finding it difficult to structure code inside my Lambda which would handle the navigation to the page, whose name I mentioned. There is no Navigation Bar and there cannot be any navigation bar. Also, for the existing code block, I am getting this error, which is quite weird to me:
Error CS0311: The type 'Xamarin.Forms.Button' cannot be used as type parameter 'T' in the generic type or method 'Android.App.Activity.FindViewById(int)'. There is no implicit reference conversion from 'Xamarin.Forms.Button' to 'Android.Views.View'. (CS0311) (AlberoPizza)
I thought this is how we usually find controls in Android app Xamarin. Can you help me complete the code for this? I'm sure it's a pretty basic type of scenario. There is this persistent error on the one hand and the code to navigate to XInfo.axml on the other. I've tried stuff like Activator.CreateInstance, the PushAsync, etc. inside the lambda, but more errors show up and entire thing becomes more and more complicated.
I want to create a GWT UI where I basically will have a single HTML page that loads a PanelA object. The user will then do their thing and eventually perform an action that will move them onto another view/screen.
I have simplified my existing views to contain just a single button which moves the user onto the next page etc. for simplicity I only have 2 views to start.
Here is my start up entry point.
public class StockWatcher implements EntryPoint
{
public void onModuleLoad()
{
final RootPanel rootPanel = RootPanel.get();
rootPanel.add( PanelA.getInstance() );
}
}
Here is the PanelA class
public class PanelA extends HTMLPanel
{
private static PanelA panel;
private PanelA()
{
super("Panel A");
final RootPanel rootPanel = RootPanel.get();
Button btnNewButton = new Button("Go to panel B");
btnNewButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event)
{
rootPanel.clear();
rootPanel.add( PanelB.getInstance() );
}
});
add(btnNewButton);
}
public static PanelA getInstance()
{
if (panel == null)
{
panel = new PanelA();
}
return panel;
}
}
My other PanelB class is pretty much the same as PanelA , ie button that brings me back to PanelA
My UI works as desired.
My Question is, Is this singleton type pattern a correct or proper way to do this? ie Have a stack of Singleton UI views that get popped on/off the main panel?
Also, what is the best way to handle hitory/breadcrumb trace through a GWT app, ie allowing a user to go back to the previous screen bearing in mind that they may navigate to PanelX from either of PanelA, PanelB or PanelC
I use "Activities and Places" to manage all of this, and it's been working quite well in production for a year or so.
https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces
I think it's fine to use a Singleton mechanism for your views, but you have to make sure to completely reset any state you store. For me, it was easier to just create new views every time the user navigates to a new spot, and then if I detected a problem with load times or something to retroactively cause the view to re-use its components. I'd advise you to get the navigation working first, and then worry about the singleton (or not) optimizations.
I recommend Activities and Places design pattern.
It covers all the issues you raise in your question, plus many more that you have not thought of (or did not ask about) yet, like native browser history management and ability to bookmark different places within the app, handling of page reloads, efficient memory management vs optimized DOM operations, scalability (building an app with dozens/hundreds of views with minimal code duplication), testability, and code-splitting (where to split large apps).
I suggest you refer "GWTP" framework to make a GWT Project, some of the features currently supported by GWTP:
Dependency injection through GIN and Guice;
Simple but powerful history management mechanism;
Support for nested presenters;
Lazy instantiation for presenter and view;
Effortless and efficient code splitting;
Integrated command pattern supporting undo/redo;
So your questions and query like Singleton UI views, best way to handle hitory/breadcrumb trace and singleton type pattern , will cover and one good framework will make easy project management.