How to open a new frame window which contains another xaml page when I click a button from the main window in universal app?
You can check out a sample on how to do this from the Offical Microsoft Samples on GitHub as can be found here but I'll summarize quickly here. Another simpler implementation can be found on Mike Taulty's Blog.
Since you haven't specified a development language I will assume C# and XAML.
Create your button in XAML which will be clicked to create a new window:
<Button
Content="Create"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Click="OnCreate" />
In the code behind, add your OnCreate click handler:
async void OnCreate(object sender, RoutedEventArgs e)
{
CoreApplicationView newCoreView = CoreApplication.CreateNewView();
ApplicationView newAppView = null;
int mainViewId = ApplicationView.GetApplicationViewIdForWindow(
CoreApplication.MainView.CoreWindow);
await newCoreView.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
newAppView = ApplicationView.GetForCurrentView();
Window.Current.Content = new SubWindowUserControl();
Window.Current.Activate();
});
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
newAppView.Id,
ViewSizePreference.UseHalf,
mainViewId,
ViewSizePreference.UseHalf);
}
This creates a new window, and fetches the application view ID of your original window for you to reference. It then awaits a dispatched thread to run (in the UI thread of the new window) to get the new view the window contains which it adds a new SubWindowUserControl to, programatically, and is then activated (you MUST remember to do this). The new window is then shown in a new standalone window using some standard parameters.
Check out the API documentation here for more details on the ApplicationViewSwitcher class.
To take this code and make it show a new XAML page you can create your new page and change the async task running on the new window in the OnCreate code as follows:
await newCoreView.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
newAppView = ApplicationView.GetForCurrentView();
Window.Current.Content = new Frame();
(Window.Current.Content as Frame).Navigate(typeof(<your_page>));
Window.Current.Activate();
});
This, instead of showing a new custom XAML element as its content, creates a new Frame which is then navigated to show your XAML page.
Hope this helps!
Related
I have discovered the limitation of displaying prompts when they are invoked from a popup window. Specifically verified with CommunityToolkit.Maui Popups.
Here's the details:
In the Map page I have this handler for the map clicked event:
void mapClicked(object sender, MapClickedEventArgs e) {
var pin = new Pin {
Label = "Here's where it is",
Location = e.Location
};
map.Pins.Add(pin);
}
I wanted to allow the user to edit the pin label when clicking on the it, like so:
pin.InfoWindowClicked += async (s, args) => {
string pinName = ((Pin)s).Label;
await DisplayPromptAsync("Enter new label", "enter new label");
};
However, this didn't work as no DisplayPrompt was shown. I tried running it in the main thread, to no avail either.
UPDATE. I've figured it out, see answer below.
The problem arises when attempting to bring up the prompt from a popup window. Evidently, one can't have a DisplayPromptAsync (or DisplayAlert for that matter) on top of a popup.
On a platform-specific level in iOS the error message reads:
Attempt to present <UIAlertController> on <Microsoft_Maui_Controls_Platform_Compatibility_ShellFlyoutRenderer> (from <Microsoft_Maui_Controls_Platform_Compatibility_ShellFlyoutRenderer>) which is already presenting <CommunityToolkit_Maui_Core_Views_MauiPopup>.
My Xamarin Forms Application has a MainPage set to
new NavigationPage(new CarsMasterDetailPage())
where CarsMasterDetailPage : MasterDetailPage.
In CarsMasterDetailPage constructor, I set the Master property to new CarsMasterPage() and the Detail property to new CarsDetailPage(). Both CarsMasterPage and CarsDetailPage extend ContentPage.
The master page contains a list of cars and a button which has an event handler that does:
await Navigation.PushAsync(new AddCar());
The AddCar page has a button with an event handler that does:
await Navigation.PopAsync();
When I first run the app, the OnAppearing method of the master page is called. The first time the navigation pops back to the master page, OnAppearing is called again. Subsequent navigation pushes and pops don't, though.
I can get around it by adding a delegate on the master page that the add car page calls when it's done, but that feels hacky since there are page events to handle this. Does anyone know why it's not working?
In my case, I did it like this and everything works fine:
My MainPage:
MainPage = new MasterDetailPage {
Master = new MasterDetailPage1Master(),
Detail = new MyNavigationPage(new MainPageLinearList(appType))
};
So the key point was to use NavigationPage in a DetailPage. After that everything works fine.
Subscribe to the NavigationPage.Popped event:
navigationPage.Popped += OnPopped;
...
void OnPopped(object sender, NavigationEventArgs e)
{
var currentPage = (sender as NavigationPage).CurrentPage;
var removedPage = e.Page;
...
}
Hi am developing a Xamarin Forms app. i have implemented local notifications in the app. When the notification has fired, upon clicking the notification it has to navigate to a particular page.
In iOS project in Appdelegate.cs i wrote this method
public async override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
which will fire when the user taps on the notification. here i need to navigate to a page. Here i wrote the below line of code
App.Current.MainPage = new NavigationPage(new FavoritesPage());
It is navigating to the Favorites page but it is just displaying a blank page. OnNavigatedTo method is not calling for the FavoritesViewModel and in the Onnavigated to am calling a method which takes id(this id comes from the notification) as parameter to get a particular favorite
Here two questions
1) How to navigate to a Specific page
2) How to pass a parameter along with the page navigation.
Can someone please help me to solve this issue.
For 1:
You want to push to a new Page, but what you did is replacing the app's MainPage. please try PushAsync. You can subscribe a MessagingCenter in App:
public App ()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
MessagingCenter.Subscribe<object, string>(this, "Push", async (sender, favoriteID) =>
{
var favorite = new FavoritesPage();
favorite.FavoriteID = favoriteID;
await (MainPage as NavigationPage).PushAsync(favorite, true);
});
}
This Lambda will fire when you call MessagingCenter.Send<object, string>(this, "Push", "01");
The string 01 here is the ID what I want to push.
For 2:
Before I push to a new page, I define a property called FavoriteID in this page, then I pass the string using method above.
Use MessagingCenter, set ContentPage type or url for MessagingCenter.Send, then MessagingCenter.Subscribe & load
I have 2 pages in my Xamarin Forms app. My first page has 4 icons in the toolbar. My second page is a login page and has a tick and a cross in the toolbar.
I can't get the login page to show any icons unless I make it a navigation page. I also have to clear ToolBarItems on the first page before calling PushAsync() otherwise it complains there are too many toolbar items.
If I call PopAsync() on the login page it does not return to the first page. I'm guessing this is due to their being 2 navigation pages. I also tried PopToRootAsync().The back button works however.
My question is - how do I show different toolbar icons on 2 different pages in a way that allows navigation to work?
I'm testing this on Windows Phone 8.0
Here is the code calling the login page:
private async void ShowLoginPage()
{
ToolbarItems.Clear();
var page = new NavigationPage(new LoginPage());
await Navigation.PushAsync(page);
}
and here is the code to return to the first page:
private void Cancel()
{
Navigation.PopToRootAsync();
}
I'm running Xamarin.Forms v1.2.2.6243
One thing you could try is to keep your Login Page inside of a NavigationPage, and then instead of running PopAsync() within the Login Page after they have logged in successfully, simply replace the MainPage with your old Navigation page:
In your App class:
public NavigationPage AppNavPage = new NavigationPage(new FirstPage());
public App() {
MainPage = AppNavPage;
}
In your FirstPage:
private async void ShowLoginPage() {
ToolbarItems.Clear();
var page = new NavigationPage(new LoginPage());
await Navigation.PushAsync(page);
}
In Login Page:
private async void OnCreateClicked(object sender, EventArgs e) {
bool loginInfoIsGood = CheckLoginInfo(); //Check their login info
if(loginInfoIsGood) {
Application.Current.MainPage = App.AppNavPage;
}
}
Otherwise, I have also done a custom renderer for the NavigationRenderer on iOS to insert toolbar items onto the right side of the Navigation Bar and have overridden some Menu related stuff on Android to change the icon text/colors.
One option that you have, and one that I implemented in my own app, is a custom renderer that removes the navigation header from the app and then you could build your own custom header. With this approach, you do lose some of the native feel of the app, and you have to implement much of the transitional functionality your self. However, it gives you alot more control over the look.
CustomRenderer that removes the navigationBar:
//add using statements
// add all view here that need this custom header, might be able to build a
//base page that others inherit from, so that this will work on all pages.
[assembly: ExportRenderer(typeof(yourView), typeof(HeaderRenderer))]
class HeaderRenderer : PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.NavigationController.SetNavigationBarHidden(true, true);
}
}
After this you can build a header view that can be placed on the top of every page (I am using xaml) so I don't know if it is relevant in you application.
Edit: You might need to change this renderer for differnt page types.
I have an app in which I have a lot of references and the load time was not acceptable to me. I have removed the splash screen image and created an animated loading screen by having a separate project with no reference to the main application which then navigates to the first page of the rest of the app. It does start up fast now but it's a little lacking still.
I would like to do another animation right before the load screen goes away. The only way I can think of to do this is to actually preload the assemblies needed for the navigation to the next page, do an animation, and then navigate.
I have tried
OnNavigatedFrom but the animation doesn't have time to run since the page will be replaced by the new page very quickly from that point.
OnNavigatingFrom is no help either as it is called as soon as I call NavigationService.Navigate();
Searching the web and Stack Overflow :)
I also considered faking it a bit by having the next page show a duplicate of the load screen and do the last animation there, but it can't match the current state of the load screen animation and is harder to maintain
Thanks for any ideas!
If you want to force the loading of an assembly, just reference a type from this assembly.
For instance, something like Console.WriteLine(typeof(YourAssembly.SomeType)); will force the loading of YourAssembly.
Now for your problem, maybe you can use usercontrols? Put the content of your main page in a user control. Display the loading page, create the usercontrol in the background, let the animation play, then when the animation is done playing replace the page's content with the usercontrol.
It turns out that you can preload by just creating a new instance of the page you are going to navigate to. Unfortunately that has to be done on the UI thread which can cause animation slowdown, at least in my experience.
Here is a sample of how to do an animation, then preload, then do another animation before navigating. :
public partial class LoadScreen : PhoneApplicationPage
{
public LoadScreen()
{
InitializeComponent();
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var sb = new Storyboard();
// create your animation here
sb.Completed += (sender, args) => PreLoad();
sb.Begin();
}
private void PreLoad()
{
// this is the part that actually takes time and causes things to get loaded
// you may need it in a try/catch block depending on what is in your constructor
var page = new PageToNavigateTo();
// now create an animation at the end of which we navigate away
var sbOut = new Storyboard();
// create your animation here
sbOut.Completed += (sender, args) => NavigateToNextScreen();
sbOut.Begin();
}
private void NavigateToNextScreen()
{
// navigate here
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// remove the loading screen from the backstack so the user doesn't see it again when hitting the back button
NavigationService.RemoveBackEntry();
}
}