Xamarin Forms: Programmatically added AbsoluteLayout not rendering on iOS - xamarin

I am trying to programmatically add an ActivityIndicator to my Xamarin ContentPages that do data access. I have all of the code working great below on my Android device (a Samsung Galaxy Tab A). When I went to test it on my iOS (iPhone 7), it does not render any of the pages that are wrapped in the AbsoluteLayout. I simplified the repo down to this project that demonstrates the issue.
The full project is here: https://github.com/wadebaird/absoluteLayoutIssue
Here are the basics, I have the ContentPage below, of which I programmatically add buttons to the "MainPageStackLayout" during OnAppearing.
<?xml version="1.0" encoding="utf-8" ?>
<absolutelayoutissue:CommonContentPage
xmlns:absolutelayoutissue="clr-namespace:AbsoluteLayoutIssue"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AbsoluteLayoutIssue.AbsoluteLayoutProgrammatic">
<absolutelayoutissue:CommonContentPage.Content>
<ScrollView>
<StackLayout x:Name="MainPageStackLayout" VerticalOptions="Start" Margin="20, 20" Spacing="25">
<Label FontSize="Large" FontAttributes="Bold" Margin="20" TextColor="Red" HorizontalTextAlignment="Center"
Text="No network connection detected, please enable a connection to use this application."
IsVisible="False" />
</StackLayout>
</ScrollView>
</absolutelayoutissue:CommonContentPage.Content>
</absolutelayoutissue:CommonContentPage>
During the constructor I call this method to programmatically add an AbsoluteLayout that wraps the ActivityIndicator and a Label:
public void AddActivityIndicator()
{
var content = this.Content;
var overlay = new AbsoluteLayout
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
};
var activityIndicator = new ActivityIndicator
{
IsEnabled = true,
HorizontalOptions = LayoutOptions.FillAndExpand,
//Use the same color as the school text color as it should contrast the background the same.
Color = Color.Black,
};
var activityMessage = new Label
{
IsEnabled = true,
HorizontalOptions = LayoutOptions.FillAndExpand,
TextColor = Color.Black,
Text = "Loading...",
};
var stackLayout = new StackLayout();
stackLayout.Children.Add(activityIndicator);
stackLayout.Children.Add(activityMessage);
activityIndicator.SetBinding(ActivityIndicator.IsVisibleProperty, nameof(ViewModel.IsBusy));
activityIndicator.SetBinding(ActivityIndicator.IsRunningProperty, nameof(ViewModel.IsBusy));
activityMessage.SetBinding(Label.IsVisibleProperty, nameof(ViewModel.IsBusy));
AbsoluteLayout.SetLayoutFlags(content, AbsoluteLayoutFlags.All);
AbsoluteLayout.SetLayoutBounds(content, new Rectangle(0f, 0f, 1, 1));
AbsoluteLayout.SetLayoutFlags(stackLayout, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(stackLayout, new Rectangle(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
overlay.Children.Add(content);
overlay.Children.Add(stackLayout);
this.Content = overlay;
}
When this is done the page loads as completely blank. When I model the page vs programmatically adding the AbsoluteLayout, it all works perfect:
<?xml version="1.0" encoding="utf-8" ?>
<absolutelayoutissue:CommonContentPage
xmlns:absolutelayoutissue="clr-namespace:AbsoluteLayoutIssue"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AbsoluteLayoutIssue.AbsoluteLayoutModeled">
<absolutelayoutissue:CommonContentPage.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<ScrollView AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<StackLayout x:Name="MainPageStackLayout" VerticalOptions="Start" Margin="20, 20" Spacing="25">
<Label FontSize="Large" FontAttributes="Bold" Margin="20" TextColor="Red" HorizontalTextAlignment="Center"
Text="No network connection detected, please enable a connection to use this application."
IsVisible="False" />
</StackLayout>
</ScrollView>
<StackLayout AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
<ActivityIndicator IsEnabled="True" HorizontalOptions="FillAndExpand" Color="Black" IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" />
<Label IsEnabled="True" HorizontalOptions="FillAndExpand" TextColor="Black" Text="Loading..." IsVisible="{Binding IsBusy}"/>
</StackLayout>
</AbsoluteLayout>
</absolutelayoutissue:CommonContentPage.Content>
</absolutelayoutissue:CommonContentPage>
However I don't want to model it as I want to be able to add this code to the fly on any page in my app.
So the question is can anyone see the difference between my modeled page and my programmatically built one? If not, has anyone seen this issue before? I can't seem to find any settings on the AbsoluteLayout to get around it.
Thank you in advance.

After a day of continuing to try and figure this out I discovered that switching around the order of setting the AbsoluteLayout on the Page.Content and then adding it's children fixed the rendering / layout issue:
overlay.Children.Add(content);
overlay.Children.Add(stackLayout);
this.Content = overlay;
needs to be like this:
this.Content = overlay;
overlay.Children.Add(content);
overlay.Children.Add(stackLayout);

Related

Xamarin Forms - how do colors work on Navigation bar for iOS

I am trying to set the BarBackgroundColor and BarTextColor for a simple XF app, and am not understanding the output. A picture of what I am seeing follows the code, and all the code can be downloaded here.
I set BarBackgroundColor, BarTextColor, and BackgroundColor below. It seems the only color (from the attached image) being set is the Color.Blue BarBackgroundColor. Shouldn't the text in the navigation bar and status bar be White? Why isn't the rest of the page Red?
App.xaml.cs
public App()
{
InitializeComponent();
var navPage = new NavigationPage(new MainPage());
navPage.BarBackgroundColor = Color.Blue;
navPage.BarTextColor = Color.White;
navPage.BackgroundColor = Color.Red;
MainPage = navPage;
}
XAML
<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="SampleApp.MainPage">
<NavigationPage.TitleView>
<StackLayout HorizontalOptions="Center"
VerticalOptions="Center">
<Label Text="Page Title" />
</StackLayout>
</NavigationPage.TitleView>
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
Here is the output I am getting.
Shouldn't the text in the navigation bar and status bar be White?
Yes , if code as shared , will not .
First , we will check how to set the text color in the navigation bar .
Because setting custom title view in Navigation Bar as follow :
<NavigationPage.TitleView>
<StackLayout HorizontalOptions="Center"
VerticalOptions="Center">
<Label Text="Page Title" />
</StackLayout>
</NavigationPage.TitleView>
Then code navPage.BarTextColor = Color.White; will not work .The reason is that , you set a custom title view in ContentPage ,and ContentPage Overrides the effect of the navigation bar text .
If want to show white color of Naviagtion Title , there are two ways based on shared code .
One is : Set TextColor for Label in custom title view .
<NavigationPage.TitleView>
<StackLayout HorizontalOptions="Center"
VerticalOptions="Center">
<Label Text="Page Title" TextColor = "White" />
</StackLayout>
</NavigationPage.TitleView>
Another is : Using original navigation bar title .
<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="SampleApp.MainPage"
Title="Page Title"> // Adding title text here
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
Then code navPage.BarTextColor = Color.White; will work for text of naviagtion bar.
Second, we will check how to set the text color in the status bar .
Here you need to enter iOS Solution folder, then open Info.plist file with Xml Editor as follow :
Then adding the Key UIViewControllerBasedStatusBarAppearance and set its value to false as follow.
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
Saving and rebuliding project , now code navPage.BarTextColor = Color.White; can work for
text color of status bar .
Why isn't the rest of the page Red?
This problem should be different for Android . If running navPage.BackgroundColor = Color.Red; in Android ,it will work.However will not in iOS.
The reason is that , Navigation Controller is different between Android and iOS. The Navigation Bar just a bar in Android , however Navigation Bar can be a whole Page view in iOS. That means if setting background color to NavigationPage in Android , it can work in Navigation Page and ContentPage. However , in iOS it only can work in Navigation page . If Want content page show different , you should individually set in Content page.
Therefore , setting as follow in iOS background color can be work.
//Adding background color here can work
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
Final effect as follow:

StackLayout GestureRecognizers Effects

I wanted to add some effects to the StackLayout GestureRecognizers so the user know they have selected the button but I don't see a way to do it? I have tried to change the BackgroundColor for the StackLayout but that is not working.
Can I change the BackgroundColor or is there a long hold event I can use ?
xaml code
<StackLayout VerticalOptions="Center"
x:Name="slpatient"
Grid.Row="4"
BackgroundColor="#8cb8e1"
Orientation="Horizontal">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="Button_Clicked" />
</StackLayout.GestureRecognizers>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="user_34.png"
Grid.Column="1"
VerticalOptions="Center" />
<Label Text="Click me to change the BackgroundColor!"
Grid.Column="2"
TextColor="White"
LineBreakMode="WordWrap"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"/>
</Grid>
</StackLayout>
cs code
private void Button_Clicked_Clicked(object sender, System.EventArgs e)
{
slpatient.BackgroundColor = Color.Black;
var masterDetailPage = Application.Current.MainPage as MasterDetailPage;
masterDetailPage.Detail = new NavigationPage((new SearchPage("DrugName")));
}
The change color functionality is likely happening on a non-UI thread. So you could potentially enclose it in Device.BeginInvokeOnMainThread so it gets called in the UI thread, as shown:
Device.BeginInvokeOnMainThread(() =>
{
slpatient.BackgroundColor = Color.Black;
});
To answer your second question, yes you can add a timer too:
private void Button_Clicked_Clicked(object sender, System.EventArgs e)
{
slpatient.BackgroundColor = Color.Black;
var masterDetailPage = Application.Current.MainPage as MasterDetailPage;
Device.StartTimer(TimeSpan.FromSeconds(10), () =>
{
masterDetailPage.Detail = new NavigationPage((new SearchPage("DrugName")));
return false; // True = Repeat again, False = Stop the timer
});
}
I have tried to change the BackgroundColor for the StackLayout but that is not working.
If you want to change background color, I suggest you can consider to use Xameffects, you can install xameffects from nuget packages. Changing TouchEffect.Color.
<ContentPage
x:Class="App4.Page21"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xe="clr-namespace:XamEffects;assembly=XamEffects">
<ContentPage.Content>
<StackLayout
xe:TouchEffect.Color="Red"
HorizontalOptions="Center"
VerticalOptions="Center">
<Button
x:Name="btn1"
Clicked="Btn1_Clicked"
HorizontalOptions="Center"
Text="btn1"
VerticalOptions="Center" />
</StackLayout>
</ContentPage.Content>
More detailed info, you can take a look:
https://github.com/mrxten/XamEffects

How to bind a nested Layout with external ContentView in Xamarin.Forms?

I have a Page with a StackLayout using BindableLayout.ItemsSource, inside each item I have a ListView, for each item in this nested ListView I need to do a Binding to a property on the Page's ViewModel. I'm trying to use the Source+Path approach but the app crashes as soon as I open the page that contains this structure.
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="BindableLayoutReferenceBug.ListViewPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BindableLayoutReferenceBug">
<StackLayout BindableLayout.ItemsSource="{Binding Items}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<local:MessageListViewTemplate />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ContentPage>
MessageListViewTemplate.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
x:Class="BindableLayoutReferenceBug.MessageListViewTemplate"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="listView">
<ListView ItemsSource="{Binding Options}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding .}" />
<Button
BackgroundColor="Blue"
Command="{Binding Source={x:Reference Name=listView}, Path=Parent.BindingContext.ShowMessageCommand}"
CommandParameter="{Binding .}"
Text="Show Message" />
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
The exception shows that there is a problem finding the reference to the x:Name I used: Can not find the object referenced by listView
This only happens when I have a nested structure as I mentioned (StackLayout with BindableLayout > ListView). I'm not sure if this is not a supported scenario or if it is a bug.
I tried using different Paths for the binding, but since this is a problem parsing the XAML not finding the x:Name referenced, I don't think it even starts evaluating my Path.
Am I doing something wrong or is this a bug in Xamarin.Forms?
Xamarin.Forms version used: 3.6.0.293080
Repro sample: https://github.com/akamud/BindableLayoutReferenceBug
Maybe, x:Name wasn't recognized at runtime even though you have set it to your content view in XAML. You could raise an issue on GitHub.
Here I tried this binding using code behind and it works fine. You could use it as an alternative:
public MessageListViewTemplate()
{
InitializeComponent();
listView.ItemTemplate = new DataTemplate(() =>
{
ViewCell viewCell = new ViewCell();
Label label = new Label();
label.SetBinding(Label.TextProperty, new Binding("."));
Button button = new Button() { Text = "Show Message", BackgroundColor = Color.Blue };
button.SetBinding(Button.CommandProperty, new Binding("Parent.BindingContext.ShowMessageCommand", source: ContentView));
button.SetBinding(Button.CommandParameterProperty, new Binding("."));
viewCell.View = new StackLayout
{
Children =
{
label,
button
}
};
return viewCell;
});
}
XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindableLayoutReferenceBug.MessageListViewTemplate"
x:Name="ContentView">
<StackLayout>
<ListView ItemsSource="{Binding Options}" x:Name="listView" HasUnevenRows="True">
</ListView>
</StackLayout>
</ContentView>
Here, I defined the parent content view as ContentView and named ListView listView.
Please notice that the list view's data template should be a view cell. So I used a view cell to wrap the content here.
It was a bug after all, it is now fixed and the above should work: https://github.com/xamarin/Xamarin.Forms/issues/6192

Xamarin Forms : How to make button appear at the bottom of a page

I am playing with a Xamarin Form trying to get a button to appear at the bottom of the page. Here is my xaml...
<?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:control="clr-namespace:RMG.InView.Device.Shared;assembly=RMG.InView.Device"
x:Class="RMG.InView.Device.Shared.PinCodeControlDemoPage">
<StackLayout>
<Label Text="Enter A Code" VerticalOptions="Center" HorizontalOptions="Center" />
<Button Text="Reveal Code" x:Name="RevealCode" Clicked="RevealCode_OnClicked" VerticalOptions="End" ></Button>
</StackLayout>
</ContentPage>
I have VerticalOptions set to End, but the button appears in the middle of the screen.
How can I make the button stick to the bottom of the screen?
With a Grid is simple just do this:
<?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:local="clr-namespace:shared_forms" x:Class="shared_forms.shared_formsPage">
<Grid>
<Label Text="Enter A Code" VerticalOptions="Center" HorizontalOptions="Center" />
<Button Text="Reveal Code" x:Name="RevealCode" HorizontalOptions="CenterAndExpand" VerticalOptions="End" />
</Grid>
</ContentPage>
With StackLayout:
<?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:local="clr-namespace:shared_forms" x:Class="shared_forms.shared_formsPage">
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" VerticalOptions="Start">
<!-- top controls -->
<Label Text="Enter A Code" VerticalOptions="Center" HorizontalOptions="CenterAndExpand" />
</StackLayout>
<StackLayout VerticalOptions="CenterAndExpand">
<!-- middle controls -->
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="End">
<!-- bottom controls -->
<Button Text="Reveal Code" x:Name="RevealCode" HorizontalOptions="CenterAndExpand" />
</StackLayout>
</StackLayout>
</ContentPage>
Result:
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout Grid.Row="0">
</StackLayout>
<StackLayout Grid.Row="1" VerticalOptions="End">
<controls:STButton Text="Yeni Görev Ekle" />
</StackLayout>
</Grid>
</ContentPage.Content>
%100 works ;)
This worked for me.
<StackLayout BackgroundColor="#2D3033">
<Button Clicked ="Button_Clicked"
Text="Login"
BackgroundColor="#007F00"
BorderColor="#004C00"
BorderWidth="1"
TextColor="white"
HorizontalOptions="CenterAndExpand"
VerticalOptions="EndAndExpand"
/>
</StackLayout>
try this for code behind :
Label EnterACodeLabel = new Label { Text = "Enter A code " };
Button RevealCodeButton= new Button { Text = "Reveal Code" };
StackLayout RevealButtonStackLayout = new StackLayout
{
VerticalOptions = LayoutOptions.End,
Children =
{
RevealCodeButton,
//put all controls want to be on button
}
};
StackLayout AllContentExceptRevelCodeButton = new StackLayout
{
Padding = new Thickness(5),
Children =
{
EnterACodeLabel ,
//put all controls need to be on the top
}
};
StackLayout AllPageContent = new StackLayout
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children =
{
AllContentExceptRevelCodeButton,
RevealButtonStackLayout
}
};
Content = AllPageContent;

Android Xamarin RendererFactory.getRenderer() does not work

For Data Template I have used StackLayout with label inside it and created a custom renderer for grid-view.
The code RendererFactory.getRenderer() doesn't work; it only shows a StackLayout without any label inside.
Any help would be appreciated.
As of now RendererFactory.getRenderer() won't work for auto layout containers such as StackLayout/GridLayout with child contents refer this link. Only the primary parent is rendered but the childrens are not rendered in UI but the controls are available after converting to native view
this._view = RendererFactory.GetRenderer(this._viewCell.View).NativeView;
Try using AbsoluteLayout and position the controls accordingly. I had tried this using XLabs Grid View Control since they used Grid View Renderer for making the UICollectionView(TileList) and GridViewCell for generating the viewCell from Forms to Native.
<controls:GridView
x:Name="GrdView"
RowSpacing="5"
Padding="5"
ColumnSpacing = "5"
ItemWidth ="152"
ItemHeight = "200"
ItemsSource="{Binding gridColl}"
>
<controls:GridView.ItemTemplate>
<DataTemplate >
<ViewCell>
<ViewCell.View>
<!-- WORKING-->
<AbsoluteLayout BackgroundColor="Red">
<Image Source="{Binding image}"
AbsoluteLayout.LayoutBounds="0, 0, 0.5, 0.5"
AbsoluteLayout.LayoutFlags="All"/>
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<Label Text="Test" BackgroundColor="Red"
AbsoluteLayout.LayoutBounds="1, 1, 0.5, 0.5"
AbsoluteLayout.LayoutFlags="All"/>
</AbsoluteLayout>
<!--NOT WORKING-->
<!--<StackLayout BackgroundColor="Blue">
<Image Source="{Binding image}" />
<Label Text="Test" BackgroundColor="Red"/>
</StackLayout>-->
</ViewCell.View>
</ViewCell>
</DataTemplate>
</controls:GridView.ItemTemplate>
</controls:GridView>
i hope this might help you...

Resources