So I'm trying to do dependency injection from the start of my app. I'm using a Flyout as the root page of my app, so I need to assign a page to Flyout.Detail. Maybe I'm missing something, but I can't set the Detail page in XAML because I can't specify the parameter in my Constructor. I can't find anything online about it.
<?xml version="1.0" encoding="utf-8" ?>
<FlyoutPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp"
xmlns:view="clr-namespace:MyApp.View"
xmlns:viewmodel="clr-namespace:MyApp.ViewModel"
x:Class="MyApp.View.FlyoutMenuRoot"
Title="FlyoutMenuRoot">
<FlyoutPage.Flyout>
<view:FlyoutMenuPage x:Name="flyoutMenuRoot" />
</FlyoutPage.Flyout>
<<FlyoutPage.Detail>
<NavigationPage>
<x:Arguments>
<view:BudgetViewPage />
</x:Arguments>
</NavigationPage>
</FlyoutPage.Detail>
</FlyoutPage>
So I'm opting to set the Detail page in the code-behind:
public partial class FlyoutMenuRoot : FlyoutPage
{
DataService dataService;
public FlyoutMenuRoot(DataService dataService)
{
InitializeComponent();
this.dataService = dataService;
Detail = new BudgetViewPage(this.dataService);
}
}
Maybe I'm missing something there as the XAML references NavigationPage before the x:Arguments.
At any rate, the Navigation bar doesn't show up at the top of the screen, so no burger icon. However, I can swipe from the left part of the screen to pull out the Flyout page. Am I missing soemthing? When I have a parameterless constructor for the Detail page and call it from the XAML, the Navigation bar and burger icon show up.
I sure do feel like I'm missing something...
you need to wrap it in a NavigationPage
Detail = new NavigationPage(new BudgetViewPage(this.dataService));
Related
We are mostly a WPF shop currently and are exploring MAUI development for future projects. We have a need to track three separate layers of content (in addition to any necessary modal content), which each navigate and may all be visible on the screen at once.
I am attempting to work through various use-cases in a test app, and am separating cases in a TabBar. I had thought to use three NavigationPages in the VM of one tab and navigate each independently, but I am stumbling over getting even one to work, let alone three.
I have tried several different approaches, including ContentPresenter and ContentView, but here is the core of my most recent approach:
public NavTestVM()
{
NavPage = new NavigationPage(new NavRoot());
}
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAndUnitTest.Views.NavTest"
x:DataType="viewModels:NavTestVM">
<TemplatedPage ControlTemplate="{Binding NavPage.CurrentPage}" HeightRequest="400" WidthRequest="600"/>
</ContentPage>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAndUnitTest.Views.NavTestViews.NavRoot"
BackgroundColor="Coral">
</ContentPage>
The NavRoot page (the root page in NavPage) does not appear in the tab. If, in App.xaml.cs, I replace
MainPage = new AppShell();
with
MainPage = new NavigationPage(new NavRoot());
I get a page full of Coral-y goodness on startup. What am I missing to get current NavigationPage content to display, outside of App.xaml.cs.MainPage?
Assuming I can get this working, I would then expect I can just repeat the approach twice more for the other layers and call it good.
I'm trying to hide the backbutton shown in the image on desktop, but no matter what I tried, then it keept showing up.
I have tried
<Shell.BackButtonBehavior>
<BackButtonBehavior IsVisible="False" IsEnabled="False" />
</Shell.BackButtonBehavior>
And I have tried following this SO post Why Back Button is hidden in Maui?
My navigation is done by saying await Shell.Current.GoToAsync(new ShellNavigationState(location), false);
Am I missing something?
So everything you need to know can be found here:
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/pages/navigationpage
NavigationPage.HasBackButton = true/false
Image (Proof)
https://gyazo.com/afbc744e7b6c5d1caa56960a536390c7
If this was helpful, mark this please as answer :)
This technique (the code in your question), added within the <ContentPage ... > declaration at the top of the xaml:
<Shell.BackButtonBehavior>
<BackButtonBehavior IsVisible="False" IsEnabled="False" />
</Shell.BackButtonBehavior>
seems to work when using the Shell to activate pages in C# codebehind (often in the BindingSource e.g., viewmodel):
await Shell.Current.GoToAsync($"{nameof(MyContentPage)}");
Adding this within the <ContentPage ...> declaration at the top of the xaml:
NavigationPage.HasBackButton="false"
seems to be applicable when using the push/pop within a NavigationPage:
await Navigation.PushAsync(new DetailsPage());
I don't use the Navigation.PushAsync. My app requires very specific navigation based on current data state, so a stack doesn't work for me.
I have verified setting the Shell.BackButtonBehavior (in the very code you provided) works in my case because I am activating pages via
await Shell.Current.GoToAsync
To enable/disable something you can use OnPlatform or OnIdiom
Exemple :
NavigationPage.HasBackButton="{OnIdiom Default='True', Desktop='False'}"
Is there a way to implement a shared TitleView component in Xamarin.Forms AppShell? I've tried a couple of approaches but it's not working as I intended.
What I'm trying to achieve is something like the following:
App Shell with a few tabs and a common Shell TitleView
The titleView is just a ContentView (let's call it Exposure) with a label to display the result of a calculation and a refresh button to fetch data and recalculate
Changing Tab shouldn't affect the state of the TitleView
What I've tried:
Initialize one instance of the Exposure component (View and ViewModel) and configure it on the DI framework to use that instance only (singleton).
When the Pages are initialized I get the single instance of the Exposure component and assign it to the Shell.TitleView property.
By doing this I was hoping that the Exposure state would be consistent across the tabs (single instance after all...), but what happens is that when I hit the refresh button it only refreshes the calculation on the current tab Exposure component, changing tab will present me with the Exposure component with old state. I'm surprised this is not supported out of the box (configure titleView at Shell level) but I might be missing something.
Might not be relevant but I'm using ReactiveUI (MVVM) and Splat (dependency injection)
The next thing I'm willing to try is to trigger a refresh when each page re-appears but that feels dodgy.
You can create a base ContentPage and set the style of TitleView of it .
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="xxx.BaseContentPage">
<NavigationPage.TitleView>
<StackLayout>
//...
</StackLayout>
</NavigationPage.TitleView>
<ContentPage.Content>
<StackLayout>
//...
</StackLayout>
</ContentPage.Content>
</ContentPage>
I have page1 with button that navigates to page2, page 2 add some messages and navigates back to page1. I want display those messages on page1. I've tried many solutions, but nothing work.
Sample code page1.xhtml:
<p:commandButton value="edit" action="#{bean1.edit}"/>
In the managed bean:
public String edit() {
return "page2?faces-redirect=true";
}
page2 managed bean
#PostConstruct
private void postConstruct() {
Faces.getFlash().setKeepMessages(true);
Messages.addFlashGlobalError("cannot edit!");
Faces.navigate("page1?faces-redirect=true");
}
Both beans are view scoped and both pages have <p:messages> at the end of body.
That can happen if the #PostConstruct is invoked too late. Apparently the bean is referenced and thus constructed for the first time relatively "late" in the view (e.g. in the very bottom). At that point, the response may be already committed which is a point of no return. You cannot navigate to a different view anymore.
You basically want to invoke the init() method before render response. With OmniFaces, you can use the following approach in page2.xhtml:
<f:metadata>
<f:viewParam name="dummy" />
<f:event type="postInvokeAction" listener="#{bean.init}" />
</f:metadata>
(you can remove the <f:viewParam name="dummy" /> if you already have your own view parameters on that page; it's just to ensure that INVOKE_ACTION phase is executed, see also postInvokeAction demo page)
and just a simple <f:event listener> method:
public void init() {
Messages.addFlashGlobalError("cannot edit!");
Faces.navigate("page1?faces-redirect=true"); // Or Faces.redirect("page1.xhtml");
}
The Faces.getFlash().setKeepMessages(true); is unnecessary as Messages#addFlashGlobalError() already does that. Please keep in mind that in Mojarra the Flash scope won't work if the navigation is to a different folder in the URL. The both pages have to be in the same folder in the URL. This is fixed in upcoming Mojarra 2.1.14.
First of all, I'm not entirely sure that the #PostConstruct is the best place to perform a redirect. See this. That being said, google turned up this and it looks reasonable. Try redirecting within the facelets page itself with a preRender event tag close to the top of the page. Cheers
Just use nevigation. It will surely work. check following code.
public String redirect() throws Exception
{
FacesContext.getCurrentInstance().addMessage("Msg1",new FacesMessage("Message 1 is"));
//FacesContext.getCurrentInstance().getExternalContext().redirect("/Sam/page2.jsf");
return "page2";
}
Hi I am new to GWT MVP pattern. I am from asp.net background and currently I am working on GWT and I am asked to create a Master page which has the menu items which should be in common to all the views. Initially I created a sample mvp project using https://developers.google.com/web-toolkit/articles/mvp-architecture-2 and in that there is navigation from one view to another. How do I maintain one view constant and keep changing the other views depending on what menu item we click. Please help
The article you mentioned is from before MVP support added in GWT. It's good at explaining the concept, but the actual implementation is less useful. To continue take a look at the GWT documentation about activities: https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces . There you will also find the solution for your problem. In brief, take a look at the ActivityManager. This manages all activities. On the activity manager you set one widget that will be static for all activities. This widget must have a method setWidget (actually it must implement AcceptsOneWidget). In each of your activity implementations you get this widget via the start method. And by calling setWidget with the specific view for that activity in the start method you set the activity specific view. This is all described very briefly, but you should get the concept if you read the mentioned documentation.
If you are working with UiBinder, your ui.xml file should be like this,
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:style>
</ui:style>
<g:DockLayoutPanel unit="EM">
<g:north size="4">
//Add Logo, menus or what you want to be displayed for all the pages
</g:north>
<g:center>
//Add code for your desired UI. In java code you change the UI by this "flowpanel"
For eg: <g:FlowPanel ui:field="flowpanel" />
</g:center>
</g:DockLayoutPanel>
Then everytime you can clear and add the widgets to be displayed in the view in the <g:center> using your java code like this flowpanel.clear();
flowpanel.add(anyWidget you need).
So <g:north> will be static view and <g:center> will be dynamic view. Now you will get the page as you desired. Since you can change everytime your view on <g:center> only.
Like this you can add <g:east>, <g:west> and g:south> if required.
If you are not working with UiBinder then do everything in your java code as follows,
final DockLayoutPanel dockLayoutPanel = new DockLayoutPanel(Style.Unit.EM);
dockLayoutPanel.addNorth(any widget you need, "4"); //NorthPanel
dockLayoutPanel.add(any widget you need); //CenterPanel
Where "4" is the size of the panel, you can change it.
Like this dockLayoutPanel.addWest(any widget you need, "4"); //WestPanel
dockLayoutPanel.addEast(any widget you need, "4");//EastPanel
dockLayoutPanel.addSouth(any widget you need, "4"); //SouthPanel
I hope you got idea now.
You divide your screen into two or more areas, and assign dedicated ActivityMapper and ActivityManager to each zone. For example, one zone can be "menu" with MenuActivityManager, and the other zone "body" with BodyActivityManager.
Here is a good explanation:
http://blog.ltgt.net/gwt-21-activities-nesting-yagni/
Note that there are both pros and cons for using this method. Browsers take milliseconds to render standard html. It may be easier to create a widget mainMenu and include it (best of all, using UiBinder) into every view, rather than deal with two activity managers.