I am trying to create a custom navigation Bar. I have set up my Navigation Bar and called it NavBar, and added a ContentView above it containing the Current Displayed page. I have created a public variable containing the ContentView like so: public ContentView CurrentPage = new Featured(); and I have a StackLayout set up in the Page like so:
var stacklayout = new StackLayout { Spacing = 0 };
stacklayout.Children.Add(CurrentPage);
stacklayout.Children.Add(NavBar);
Content = stacklayout;
I have a function that is called when a button on my NavBar is pressed to change the value of CurrentPage, set up like this:
void NavToBuy(object sender, EventArgs e)
{
CurrentPage = new Buy();
}
But when the button is pressed, nothing happens. I think that the value is being changed, but not displayed on the screen. Is there any way to fix this? Thanks for any help in advance :)
This answer was taken from reddit user brminnick1
You need to replace CurrentPage inside of the StackLayout, which would
look something like this:
Device.BeginInvokeOnMainThread(() => stackLayout.Children[0] = new Buy());
Related
I'm building my first full-scale Xamarin.Forms app and trying to figure out how to keep user input between navigation. After doing some searching online I've read that the default behavior is to completely reload pages each time you navigate, but you can change the default behavior by setting the NavigationCacheMode to true or required, but I've tried to set this attribute in both xaml and C# with no success - it seems like the property is not recognized.
Is there a simple way to make it so that user input does not disappear when navigating between pages? If anyone can show me how to set the NavigationCacheMode that would be great, but I'm also open to any reasonable solution that will keep the user input from disappearing during navigation.
Additional details: My app has a UWP and Android project. I am using a master detail page for navigation. Here is my MenuList_ItemSelected event handler:
private void MenuList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = (MenuItem)e.SelectedItem;
var title = item.Title;
var page = (Page)Activator.CreateInstance(item.TargetType);
Detail = new NavigationPage(page); //TODO: when menu item is clicked and you're already on that page, the menu should just slide back. (currently it does nothing and stays out).
IsPresented = false;
}
Finally was able to solve this! I adapted this code from a related post which implements a Dictionary that keeps track of the navigation stack:
In the constructor for my Master Detail Page:
public partial class MenuPage : MasterDetailPage
{
Dictionary<Type, Page> menuCache = new Dictionary<Type, Page>();
}
Then in the ItemSelected method:
private void MenuList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (menuCache.Count == 0)
menuCache.Add(typeof(AttendancePage), Detail);
var item = (MenuItem)e.SelectedItem;
if (item != null)
{
if (menuCache.ContainsKey(item.TargetType))
{ Detail = menuCache[item.TargetType]; }
else
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
menuCache.Add(item.TargetType, Detail);
}
menuList.SelectedItem = null; //solves issue with nav drawer not hiding when same item is selected twice
IsPresented = false;
}
}
I want to hide Nav bar on the main-start page of my app. but want to make it display with a title and back button on the second page. This is something I did, but it appears on the main page too.
In App.xaml.cs, I used this:
MainPage = new NavigationPage(new MainPage());
In corresponding login page, I created this:
private async void LoginPage_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new LoginPage());
}
When the user will click it'll take to the second page(login page).
Also, how can I change the color of the nav bar?
In the constructor of the page you want to hide the navigation bar, use the following code.
Xamarin.Forms.NavigationPage.SetHasNavigationBar(this, false);
To change the color of the NavigationBar, you can do this.
new NavigationPage(new MainPage())
{
BarBackgroundColor = Color.FromHex("#000000"),
BarTextColor = Color.White
};
As a bonus, you can also remove it via xaml:
<ContentPage ...namespaces... NavigationPage.HasNavigationBar="False">...
In App.cs use
MainPage = new YourProjectNamespace.MainPage();
This will hide your navigation bar on your first page
i already searched few stackoverflow threads related thisbut i have problem in code plz review my code and tell me where is the problem i am stuck and do not where is the problem i am new here please help me out
public partial class MainWindow : Window
{
Grid MainGrid = new Grid();
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
TextBox dynamicTextBox = new TextBox();
dynamicTextBox.Text = "Type Partnumber";
Grid.SetRow(dynamicTextBox, 1);
Grid.SetColumn(dynamicTextBox, 0);
this.MainGrid.Children.Add(dynamicTextBox);
}
}
By using the button_Click event you are adding the dynamic TextBox to the MainGrid and that code looks fine. But the problem is that this.MainGrid is not in the Present UI, its similar to the dynamically created TextBox, since you ware defined it in the code-behind(see the definition above the constructor Grid MainGrid = new Grid();).
Consider that canContainer is a canvas defined in the xaml as like the following,
<Canvas Height="319" Margin="0"
Width="517"
Name="canContainer"/>
To overcome this you can choose any of the following method.
1.Add The MainGrid to the UI. it will add the grid to the canvas, keep in mind the dynamic textBox already added to the Grid.
which means the code should be like this -->
this.canContainer.Children.Add(MainGrid);
2.Add dynamicTextBox to any other parent which is present in the UI.
this.canContainer.Children.Add(dynamicTextBox);
You you are using this method then need not to define and adding MainGrid
I'm having quite a few other problems with layout that require a lot of extra InvalidateLayout() calls, so I'm starting to question if I understand how RelativeLayout works.
Here's a very simple example of a UI that wants a right-aligned label:
public class MainPage : ContentPage {
public MainPage() {
var layout = new RelativeLayout();
var label = new Label() {
Text = "I want to be right-aligned."
};
layout.Children.Add(label,
Constraint.RelativeToParent((rl) => rl.Width - label.Width),
Constraint.Constant(10));
var button = new Button() {
Text = "Invalidate"
};
button.Clicked += (object sender, EventArgs e) => layout.ForceLayout();
layout.Children.Add(button,
Constraint.Constant(10),
Constraint.Constant(10));
Content = layout;
}
}
I expect this to start with the label properly aligned, but it does not align the label correctly until another layout pass is forced. By overriding methods like OnSizeRequest() in my custom control, I've determined this is because the calls to OnSizeRequest() don't happen until after the calls to the RelativeLayout's constraint lambdas. So, when the page is laid out, the label's Width is -1. When ForceLayout() is called later, the Label has had a chance to perform its layout logic and has properly set the Width property, so it gets laid out correctly.
In a larger context, I'm trying to make a button that, when clicked, fades out and a label slides into place where it was. It's to be aligned in the bottom-right corner of my layout, but I'm finding that modifying Opacity or IsVisible only inconsistently updates the layout. The only consistent behavior is RelativeLayout really likes to ask for the control's size before it gets a chance to resize itself.
Am I interpreting how to use a RelativeLayout wrong, or is this a mistake in its logic?
Delving deep into the (current) implementation of RelativeLayout, I found a truth I did not expect: it does not consult a view's GetSizeRequest() method or call its Layout() method before the constraints are calculated, because those constraints might affect the control's final size. The consequence: while the constraints are being calculated, the control's bounds reflect its old position and size.
To "fix" this, call the view's GetSizeRequest() inside constraints that need the most up-to-date size of the control:
public class MainPage : ContentPage {
public MainPage() {
var layout = new RelativeLayout();
var label = new Label() {
Text = "I want to be right-aligned."
};
Func<RelativeLayout, double> getLabelWidth = (parent) => label.GetSizeRequest(parent.Width, parent.Height).Request.Width;
layout.Children.Add(label,
Constraint.RelativeToParent((rl) => rl.Width - getLabelWidth(rl)),
Constraint.Constant(10));
var button = new Button() {
Text = "Invalidate"
};
button.Clicked += (object sender, EventArgs e) => layout.ForceLayout();
layout.Children.Add(button,
Constraint.Constant(10),
Constraint.Constant(10));
Content = layout;
}
}
I can create a new element of type Page in code behind, but I want to add children elements of type UIElement as I would to a Grid like: myGrid.Children.Add(myUIElement);
I don't have the Children property and setting the Content property does not work.How can I achieve this?
UPDATE:
This is what I have so far, but does not work:
Page myNewPage = new Page();
Grid myGrid = new Grid();
TextBlock myText = new TextBlock();
myText.Text = "I am a TextBlock!";
myGrid.Children.Add(myText);
myNewPage.Content = myGrid;
Frame.Navigate(myNewPage.GetType());
A Page can host a single UIElement in its Content property. To add several children, you have to do it like you would do it in XAML: add a panel that can contain and layout several children and add your elements to that panel.
In your question you talk about a grid called myGrid. You could add and layout your items in myGrid and then set myGrid as yourPage.Content.
Your code correctly builds the page. The problem with your code is that your Frame is navigating to a new instance of Page that is not the one that you created. Frame creates a new instance of the type that you pass as a parameter.
If you want to create a page fully in code, you can simply create class that extends Page and build the page in its constructor:
public class MyPage : Page
{
public MyPage()
{
this.BuildPage();
}
private void BuildPage()
{
var panel = new StackPanel();
panel.Children.Add(new TextBlock { Text = "Hello" });
panel.Children.Add(new TextBlock { Text = "World" });
this.Content = panel;
}
}
That's, after all, what the InitializeComponent method does in pages created in XAML.