Image overtaking entire screen? - image

I have this code to create my page, but for some reason the image os taking the entire page and not displaying any of the other content
here is the picture of what i am getting. the image lays on top of all the other content despite the image being inside of the stacklayout
namespace GarageSale.Views
{
public class abcChapterPage : basePage
{
myDataTypes.abcChapter abc;
StackLayout baseStack;
#region Views
Label lblName = new Label
{
Text = "",
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
Label lblSchool = new Label
{
Text = "",
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
Label lblLocation = new Label
{
Text = "",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
Image profImg = new Image
{
Scale = .1f,
Aspect = Aspect.AspectFit,
};
Button viewItems = new Button
{
Text = " View Items ",
BorderRadius = 0,
//Margin = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
Button viewMembers = new Button
{
Text = "View Members",
BorderRadius = 0,
//Margin = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
#endregion
public abcChapterPage(myDataTypes.abcChapter o)
{
abc = o;
populateProfileFields();
}
public abcChapterPage()
{
shouldGetChapter = true;
abcid = int.Parse(App.CredManager.GetAccountValue("abc_chapter_id"));
}
StackLayout makeGUI()
{
viewItems.Command = new Command(() =>
{
Navigation.PushAsync(new viewListPage(new itemListView(), abc.id, 1, "Items for sale by " + abc.school));
});
viewMembers.Command = new Command(() =>
{
Navigation.PushAsync(new viewListPage(new userListView(), abc.id, 2, " abc Members " + abc.school));
});
#region basestack
return new StackLayout
{
VerticalOptions = LayoutOptions.Fill,
HorizontalOptions = LayoutOptions.Fill,
Children = {
//lblName,
profImg,
lblSchool,
lblLocation,
new StackLayout {
Orientation = StackOrientation.Horizontal,
Padding = 0,
// Margin = 0,
Spacing = 0,
Children = {
viewItems,
viewMembers
}
}
}
};
#endregion
}
public void populateProfileFields()
{
baseStack = makeGUI();
Title = abc.school + " abc";
lblSchool.Text = abc.school;
lblLocation.Text = abc.city +", "+ abc.state;
profImg.Source = ImageSource.FromStream(() => new MemoryStream(abc.picture));
Content = baseStack;
//TODO: do this if user is admin
//if (Constants.AdminUsers.Contains(App.CredManager.GetAccountValue("G_id")) && !adminAlert)
//{
// await Task.Delay(5000);
// DisplayAlert("Admin Notice:", "You are logged in as an administrative user!", "Dismiss");
// adminAlert = true;
//}
}
bool shouldGetChapter = false;
int abcid;
protected async override void OnAppearing()
{
if (shouldGetChapter)
{
abc = await App.MANAGER.YSSI.GetabcChapter(abcid);
populateProfileFields();
}
}
}

Since default TextColor is black like your background Color its not visible.
The reason that image goes center is might be the Scale = .1f, you should remove that.

You may misunderstood the use of Aspect in Xamarin.Forms, the Image control either fills container without keeping aspect ratio, clips the image or letter-boxing it. It doesn't resize itself, the image keeps its aspect ratio.
In your case, your image's container is the entire StackLayout, so the image covers the other controls. If you want to resize the the image, there are many ways, besides changing the image source itself, the most simple ways here should be setting a fixed size to the image control, but if you want to create a adapt layout for most different sizes of device, in this scenario, I suggest to put a Xamarin.Forms.Grid, and set RowDefinitions and ColumnDefinitions for it.

Related

Is it possible to add a SwipeView to a grid row is Xamarin Forms

I am building a grid in the C# code of my Xamarin Forms app. I want to provide the user with ability to swipe on a row and have a swipeview tray display. I have tried to follow the documentation here - https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/swipeview.
Below is the code I have:
private void AddSwipeControl(int pi_RowNumber)
{
try
{
//Add the swipeview
//Add a swip control to the and have it go all the way across. See if this works
// SwipeItems
SwipeItem tobj_DeleteSwipeItem = new SwipeItem
{
Text = "Delete",
BackgroundColor = Color.LightGreen
};
tobj_DeleteSwipeItem.Invoked += tobj_DeleteSwipeItem_Invoked; ;
List<SwipeItem> tobj_swipeItems = new List<SwipeItem>() { tobj_DeleteSwipeItem };
// SwipeView content
Label swipeContent = new Label()
{
BackgroundColor = Color.Pink,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
var tobj_SwipeGestureRecognizer = new SwipeGestureRecognizer() { Direction = SwipeDirection.Right };
tobj_SwipeGestureRecognizer.Swiped += tobj_SwipeGestureRecognizer_Swiped;
swipeContent.GestureRecognizers.Add(tobj_SwipeGestureRecognizer);
SwipeView swipeView = new SwipeView
{
LeftItems = new SwipeItems(tobj_swipeItems) { Mode = SwipeMode.Reveal },
Content = swipeContent
};
//Add the swipeview to the grid
grdDataGrid.Children.Add(swipeView, 0, pi_RowNumber);
Grid.SetColumnSpan(swipeView, grdDataGrid.ColumnDefinitions.Count);
}
catch (Exception ex)
{
SharedErrorHandler.ProcessException(ex);
}
}
private void tobj_SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
{
try
{
if (e.Direction == SwipeDirection.Right)
{
var tobj_SwipeView = ((Label)sender).Parent as SwipeView;
tobj_SwipeView.Open(OpenSwipeItem.LeftItems);
}
}
catch (Exception ex)
{
SharedErrorHandler.ProcessException(ex);
}
}
I can see the content of my swipeview in my grid row exactly where I want it and the swipe event of the label control fires as expect. However the swipe tray never opens even if I attempt to programmatically open it using tobj_SwipeView.Open(OpenSwipeItem.LeftItems); This leads me to think I am doing something wrong in building my swipe view. I have tested on Android and UWP. Any ideas what I am doing wrong?.
UPDATE: So this only appears to be a challenge on UWP. Below is my updated code. I have a button on the screen I press to run the Button_Clicked event.
private void tobj_SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
{
try
{
if (e.Direction == SwipeDirection.Right)
{
var tobj_SwipeView = ((Label)sender).Parent as SwipeView;
tobj_SwipeView.Open(OpenSwipeItem.LeftItems);
}
}
catch (Exception ex)
{
//SharedErrorHandler.ProcessException(ex);
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
private void Button_Clicked(object sender, EventArgs e)
{
try
{
//First add two rows
grdDataGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
grdDataGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
//First add some data columns to the grid
grdDataGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto});
grdDataGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
grdDataGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
//Add data to each row
grdDataGrid.Children.Add(new Label() { Text = "Row 0 Col 0" }, 0 , 0);
grdDataGrid.Children.Add(new Label() { Text = "Row 0 Col 1" }, 1, 0);
grdDataGrid.Children.Add(new Label() { Text = "Row 0 Col 2" }, 2, 0);
grdDataGrid.Children.Add(new Label() { Text = "Row 1 Col 0" }, 0, 1);
grdDataGrid.Children.Add(new Label() { Text = "Row 1 Col 1" }, 1, 1);
grdDataGrid.Children.Add(new Label() { Text = "Row 1 Col 2" }, 2, 1);
var ti_RowNumber = -1;
foreach (RowDefinition tobj_Row in grdDataGrid.RowDefinitions)
{
ti_RowNumber += 1;
//Add the swipeview
//Add a swip control to the and have it go all the way across. See if this works
// SwipeItems
SwipeItem tobj_DeleteSwipeItem = new SwipeItem
{
Text = "Delete",
BackgroundColor = Color.LightGreen
};
tobj_DeleteSwipeItem.Invoked += OnDeleteSwipeItemInvoked; ;
List<SwipeItem> tobj_swipeItems = new List<SwipeItem>() { tobj_DeleteSwipeItem }; //comment out
// SwipeView content
Label swipeContent = new Label()
{
BackgroundColor = Color.Pink,
Text = "test",
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
var tobj_SwipeGestureRecognizer = new SwipeGestureRecognizer() { Direction = SwipeDirection.Right };
tobj_SwipeGestureRecognizer.Swiped += tobj_SwipeGestureRecognizer_Swiped;
swipeContent.GestureRecognizers.Add(tobj_SwipeGestureRecognizer);
SwipeView swipeView = new SwipeView
{
LeftItems = new SwipeItems(tobj_swipeItems) { Mode = SwipeMode.Reveal },
Content = swipeContent
};
//Add the swipeview to the grid
grdDataGrid.Children.Add(swipeView, 0, ti_RowNumber);
Grid.SetColumnSpan(swipeView, grdDataGrid.ColumnDefinitions.Count);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
This seems to work fine on iOS and Android but on UWP, the swipe event for the label fires but the Swipe tray never opens. Is this a known UWP challenge?
I create a simple demo based on your code , and it works properly on my side.
You can refer to the following code:
<StackLayout Margin="20">
<Grid x:Name="grdDataGrid" VerticalOptions="FillAndExpand">
<Grid.RowDefinitions >
<RowDefinition Height="60" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Button Text="add" Clicked="Button_Clicked" />
</StackLayout>
The c# code is:
private void AddSwipeControl()
{
try
{
//Add the swipeview
//Add a swip control to the and have it go all the way across. See if this works
// SwipeItems
SwipeItem tobj_DeleteSwipeItem = new SwipeItem
{
Text = "Delete",
BackgroundColor = Color.LightGreen
};
tobj_DeleteSwipeItem.Invoked += OnDeleteSwipeItemInvoked; ;
List<SwipeItem> tobj_swipeItems = new List<SwipeItem>() { tobj_DeleteSwipeItem }; //comment out
//SwipeItems tobj_swipeItems = new SwipeItems();
//tobj_swipeItems.Add(tobj_DeleteSwipeItem);
// SwipeView content
Label swipeContent = new Label()
{
BackgroundColor = Color.Pink,
Text = "test",
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
var tobj_SwipeGestureRecognizer = new SwipeGestureRecognizer() { Direction = SwipeDirection.Right };
tobj_SwipeGestureRecognizer.Swiped += tobj_SwipeGestureRecognizer_Swiped;
swipeContent.GestureRecognizers.Add(tobj_SwipeGestureRecognizer);
SwipeView swipeView = new SwipeView
{
LeftItems = new SwipeItems(tobj_swipeItems) { Mode = SwipeMode.Reveal },
Content = swipeContent
};
//Add the swipeview to the grid
grdDataGrid.Children.AddVertical(swipeView);
// grdDataGrid.Children.Add(swipeView, 0, pi_RowNumber);
// Grid.SetColumnSpan(swipeView, grdDataGrid.ColumnDefinitions.Count);
}
catch (Exception ex)
{
// SharedErrorHandler.ProcessException(ex);
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
private void tobj_SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
{
try
{
if (e.Direction == SwipeDirection.Right)
{
var tobj_SwipeView = ((Label)sender).Parent as SwipeView;
tobj_SwipeView.Open(OpenSwipeItem.LeftItems);
}
}
catch (Exception ex)
{
//SharedErrorHandler.ProcessException(ex);
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
Note:
When adding the SwipeControl to Grid, I used code:
grdDataGrid.Children.AddVertical(swipeView);
And to simplify the code,I removed the parameter of function AddSwipeControl().

Carousel View in Xamarin Forms NOT Loading different Templates

public DataTemplate CreateQuestionAnswerRadioButtonTemplate(string question, List<string> answers){
DataTemplate template = new DataTemplate(() =>
{
StackLayout parentLayout = new StackLayout()
{
Padding = new Thickness(20, 20, 20, 20),
HeightRequest = 500,
};
ScrollView surveyScrollView = new ScrollView()
{
Orientation = ScrollOrientation.Vertical,
};
StackLayout questionLayout = new StackLayout()
{
Padding = new Thickness(5, 5, 5, 5),
HeightRequest = 500,
};
Label questLabel = new Label();
questLabel.Text = question;
questLabel.TextColor = Color.FromHex("#EF4D80");
questLabel.FontAttributes = FontAttributes.Bold;
questLabel.FontSize = 18;
BindableRadioGroup radioGroup = new BindableRadioGroup(false);
radioGroup.ItemsSource = answers;
questionLayout.Children.Add(questLabel);
questionLayout.Children.Add(radioGroup);
surveyScrollView.Content = questionLayout;
parentLayout.Children.Add(surveyScrollView);
return parentLayout;
});
return template;
}
Adding these Data Templates to a List.
new CarouselView
{
Margin = new Thickness(0, 20, 0, 0),
ItemsSource = dataTemplates,
ItemTemplate = dataTemplates[0],
};
Now when I swipe the Carousel, How do I load dataTemplates[1 or 2 or 3] ??
I have a Next Button in which in am setting the item source of the Carousel View to dataTemplates[1] but the template does not get updated
Pls Suggest the right approach ?
dataTemplates = new List<DataTemplate>();
dataTemplates.Add(CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_1, SurveyQuestion_1_Answers));
dataTemplates.Add(CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_3, SurveyQuestion_3_Answers));
dataTemplates.Add(CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_4, SurveyQuestion_4_Answers));
dataTemplates.Add(CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_5, SurveyQuestion_5_Answers));
You need a DataTemplateSelector for your CarouselView.
in your code behind of the page:
new CarouselView
{
Margin = new Thickness(0, 20, 0, 0),
ItemsSource = dataTemplates,
ItemTemplate = new SurveyDataTemplateSelector()
};
SurveyDataTemplateSelector
DataTemplate survey1Template;
DataTemplate survey3Template;
DataTemplate survey4Template;
DataTemplate survey5Template;
public SurveyDataTemplateSelector()
{
survey1Template = CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_1, SurveyQuestion_1_Answers);
survey3Template = CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_3, SurveyQuestion_3_Answers);
survey4Template = CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_4, SurveyQuestion_4_Answers);
survey5Template = CreateQuestionAnswerRadioButtonTemplate(Constants.SurveyQuestion_5, SurveyQuestion_5_Answers);
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
// Here you define which DataTemplate is selected, for example:
if (item == null)
{
return null;
}
SurveyAnswers answers = item as SurveyAnswers;
if (answers.question == 1)
{
return survey1Template;
}
else if (answers.question == 3)
{
return survey3Template;
}
else if (answers.question == 4)
{
return survey4Template;
}
else if (answers.question == 5)
{
return survey5Template;
}
return null;
}
Check this link for a good documentation by Xamarin: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/templates/data-templates/selector/

How to Add Content Page to Segment Control in IOS Xamarin.Forms

I have used Segmented Control in my application. I don't know how to add two content pages to Segment control like a tabbed page. I have attached the sample file. Please give any suggestion Link for Sample Application
Sample Code:
public partial class SamplePage : ContentPage
{
SegmentedControl segControl;
SegmentedControlOption optionOne;
SegmentedControlOption optionTwo;
public SamplePage()
{
segControl = new SegmentedControl();
optionOne = new SegmentedControlOption();
optionTwo = new SegmentedControlOption();
optionOne.Text = "One";
optionTwo.Text = "Two";
segControl.Children.Add(optionOne);
segControl.Children.Add(optionTwo);
var stack = new StackLayout()
{
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children = { segControl }
};
this.Content = stack;
}
}
ScreenShot Attached
Just some suggestions and explanations.
We can't put a ContentPage inside another ContentPage
It's better to use ContentView instead of ContentPage
Grid is more recommended in this scenario , since it fills with the whole Screen.
Use ValueChanged event to change the view dynamically.
Code :
Page
public partial class SegmentedAppPage : ContentPage
{
SegmentedControl segControl;
SegmentedControlOption scOptionOne;
SegmentedControlOption scOptionTwo;
Grid grid;
View1 view1 = new View1();
View2 view2 = new View2();
public SegmentedAppPage()
{
InitializeComponent();
segControl = new SegmentedControl();
segControl.SelectedValue = "One";
scOptionOne = new SegmentedControlOption();
scOptionTwo = new SegmentedControlOption();
scOptionOne.Text = "One";
scOptionTwo.Text = "Two";
segControl.Children.Add(scOptionOne);
segControl.Children.Add(scOptionTwo);
grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.Children.Add(segControl, 0, 0);
grid.Children.Add(view1, 0, 1);
this.Content = grid;
segControl.ValueChanged += SegControl_ValueChanged;
}
private void SegControl_ValueChanged(object sender, EventArgs e)
{
SegmentedControl control = sender as SegmentedControl;
if(control.SelectedValue is "One")
{
grid.Children.Remove(view2);
grid.Children.Add(view1,0,1); //This line
}
else if (control.SelectedValue is "Two")
{
grid.Children.Remove(view1);
grid.Children.Add(view2, 0, 1); //This line
}
this.Content = grid;
}
}
ContentView
public class View1 : ContentView
{
public View1()
{
Content = new StackLayout
{
BackgroundColor = Color.Green,
Children = {
new Label { Text = "View1" }
}
};
}
}
To set default value on segmentedControl , modify code in SegmentedControlRenderers
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
base.OnElementChanged(e);
var segmentedControl = new UISegmentedControl();
for (var i = 0; i < e.NewElement.Children.Count; i++)
{
segmentedControl.InsertSegment(e.NewElement.Children[i].Text, i, false);
}
segmentedControl.ValueChanged += (sender, eventArgs) => {
e.NewElement.SelectedValue = segmentedControl.TitleAt(segmentedControl.SelectedSegment);
};
segmentedControl.SelectedSegment = 0; // add this line
SetNativeControl(segmentedControl);
}
Test

ListView Crashing when reloading with my custom cell

I have an app with a Xamarin forms listview that's fed by an ObservableCollection to list user games I fetch from my server. The first time I load it up it works just fine. If I modify that existing ObservableCollection in any way my app crashes with a NullException. I've narrowed it down to my custom ViewCell (GameCell) and I've posted that below. If I comment one line out toward the bottom:
this.SetBinding(IsEnabledProperty, "Enabled");
Then it no longer crashes updating the ObservableCollection, but then I no longer get the functionality to have some cells enabled and some cells disabled. I'm guessing it's trying to set the isEnabled property to a cell that no longer exists because it's been removed on my reload. Any ideas how do this with out crashing? BTW, this works fine on Android, just not on iOS
using System;
using Xamarin.Forms;
using FFImageLoading.Forms;
namespace Upwords
{
public class GameCell : ViewCell
{
public GameCell ()
{
var icon = new CachedImage () {
Aspect = Aspect.AspectFit,
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
BackgroundColor = AppStyle.IconColor,
IsVisible = false,
};
icon.SetBinding (CachedImage.SourceProperty, "Icon");
icon.SetBinding (CachedImage.LoadingPlaceholderProperty, "IconPlaceholder");
icon.SetBinding (CachedImage.ErrorPlaceholderProperty, "IconPlaceholder");
icon.SetBinding (CachedImage.WidthRequestProperty, "Height");
icon.SetBinding (CachedImage.HeightRequestProperty, "Height");
icon.Success += (object sender, CachedImageEvents.SuccessEventArgs e) => {
icon.FadeAnimationEnabled = false;
if(icon.Source.GetType() == typeof(Xamarin.Forms.UriImageSource)) {
icon.BackgroundColor = Color.Transparent;
}
};
icon.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(icon.Source != null)
icon.IsVisible = true;
else
icon.IsVisible = false;
};
var nameLabel = new Label () {
FontFamily = "Helvetica",
FontAttributes = FontAttributes.Bold,
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
TextColor = Color.Black,
};
nameLabel.SetBinding (Label.TextProperty, "Name");
//Hide label if it's blank
nameLabel.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(nameLabel.Text == "")
nameLabel.IsVisible = false;
else
nameLabel.IsVisible = true;
};
var detailsLabel = new Label () {
FontFamily = "Helvetica",
FontSize = Device.GetNamedSize (NamedSize.Small, typeof(Label)),
FontAttributes = FontAttributes.Bold,
TextColor = Color.FromHex ("#666"),
IsVisible = false,
};
detailsLabel.SetBinding (Label.TextProperty, "Details");
//Hide label if it's blank
detailsLabel.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(string.IsNullOrEmpty( detailsLabel.Text ) )
detailsLabel.IsVisible = false;
else
detailsLabel.IsVisible = true;
};
var textLayout = new StackLayout {
Padding = new Thickness (5, 0, 0, 0),
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
Children = { nameLabel, detailsLabel}
};
var optionSwitch = new Switch() {
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center,
IsVisible = false,
};
optionSwitch.SetBinding (Switch.IsVisibleProperty, "IsSwitch");
var statusLayout = new StackLayout {
Padding = new Thickness (10, 10, 10, 10),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { icon, textLayout, optionSwitch}
};
statusLayout.SetBinding (StackLayout.HeightRequestProperty, "Height");
var separatorBox = new BoxView {
HeightRequest = 1,
HorizontalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.FromRgb(.75,.75,.75),
};
separatorBox.SetBinding (BoxView.HeightRequestProperty, "SeparatorHeight");
var separatorBoxLayout = new StackLayout {
Padding = new Thickness (50, 0, 0, 0),
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { separatorBox }
};
var cellLayout = new StackLayout {
Spacing = 0,
Padding = new Thickness (0, 0, 0, 0),
Orientation = StackOrientation.Vertical,
HorizontalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.White,
Opacity = 1.0,
Children = { statusLayout, separatorBoxLayout }
};
cellLayout.SetBinding (StackLayout.OpacityProperty, "Opacity");
var headerFrame = new ListViewLabel () {
};
headerFrame.SetBinding (ListViewLabel.IsVisibleProperty, "IsLabel");
headerFrame.SetBinding (ListViewLabel.TextProperty, "Name");
headerFrame.SetBinding (ListViewLabel.HeightRequestProperty, "Height");
headerFrame.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(headerFrame.IsVisible)
cellLayout.IsVisible = false;
else
cellLayout.IsVisible = true;
};
var paddedLayout = new StackLayout {
Spacing = 0,
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = { headerFrame, cellLayout }
};
this.SetBinding(IsEnabledProperty, "Enabled");
this.View = paddedLayout;
}
}
public class GameCellData
{
public string Icon { get; set; }
public string IconPlaceholder { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public int Height { get; set; }
public int SeparatorHeight { get; set; }
public bool Enabled { get; set; }
public double Opacity { get; set; }
public bool IsLabel { get; set; }
public bool IsSwitch { get; set; }
}
}
Turns out updating to latest packages fixed the crash.

xamarin forms dynamically adding custom font labels to scrollview is extremely slow

So I have a horizontal scrollview that I'm trying to dynamically populate when the user takes a certain action. The items I am throwing into the view each contain 4 labels that are using custom fonts. When I try to add about 10 of these items it lags for about 1.5 seconds on android and 1 second on IOS. If I take the custom font out then its about 1 second on each platform. If I take out 3 of the labels and only display one then its almost instantaneous. Is there any known reason for the lag? And is there any way around it so I can still use a custom font without a huge lag?
Here's a quick sample I made that pretty much does what I'm doing in my app. However, my app has more stuff so the lag isn't quite as bad here but it is still very noticeable
public class App : Application
{
public int count;
public ScrollView scroll, scroll2, scroll3;
public App ()
{
count = 1;
scroll = new ScrollView {
VerticalOptions = LayoutOptions.Center,
Orientation = ScrollOrientation.Horizontal
};
scroll2 = new ScrollView {
VerticalOptions = LayoutOptions.Center,
Orientation = ScrollOrientation.Horizontal
};
Button button = new Button(){
Text = "click",
};
button.Clicked += (sender, e) => AddStuff();
Button button2 = new Button(){
Text = "click",
};
button2.Clicked += (sender, e) => AddStuff2();
MainPage = new ContentPage {
BackgroundColor = Color.White,
Content = new StackLayout{
Children={
button,
scroll,
button2,
scroll2
}
}
};
}
//this one is instantaneous
public void AddStuff()
{
StackLayout stack = new StackLayout () {
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 200,
};
for (int i = 0; i < 11; i++)
stack.Children.Add (
new StackLayout(){
Children = {
new Label (){TextColor = Color.Blue, Text = "Size: ", WidthRequest = 100 },
}
}
);
scroll.Content = stack;
count++;
}
//this one takes forever
public void AddStuff2()
{
StackLayout stack = new StackLayout () {
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 200,
};
for (int i = 0; i < 11; i++)
stack.Children.Add (
new StackLayout(){
Children = {
new Label (){TextColor = Color.Blue, Text = "Size: ", WidthRequest = 100 },
new Label (){TextColor = Color.Blue, Text ="" + count*i, WidthRequest = 100 },
new Label (){TextColor = Color.Blue, Text = "Size: ", WidthRequest = 100 },
new Label (){TextColor = Color.Blue, Text ="" + count*i, WidthRequest = 100 }
}
}
);
scroll2.Content = stack;
count++;
}
}
and the custom font label for droid
[assembly: ExportRenderer (typeof (Label), typeof (CustomFontLabel_Droid))]
namespace df.Droid
{
public class CustomFontLabel_Droid:LabelRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.Label> e) {
base.OnElementChanged (e);
var label = (TextView)Control;
Typeface font = Typeface.CreateFromAsset (Forms.Context.Assets, "SourceSansPro-Semibold.otf");
label.Typeface = font;
}
}
}
Just incase anyone else is having a similar problem, if you make a static typeface property in the android MainActivity instead of calling createFromAsset inside the Label.OnElementChanged function every time then it gets rid of the extra lag on android.
CustomFontLabel_Droid.cs
[assembly: ExportRenderer (typeof (Label), typeof (CustomFontLabel_Droid))]
namespace df.Droid
{
public class CustomFontLabel_Droid:LabelRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.Label> e) {
base.OnElementChanged (e);
var label = (TextView)Control;
// this guy slows things down-> Typeface font = Typeface.CreateFromAsset (Forms.Context.Assets, "SourceSansPro-Semibold.otf");
label.Typeface = MainActivity.semiBoldFont;
}
}
}
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
public static Typeface semiBoldFont = null;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.Init (this, bundle);
LoadApplication (new App ());
semiBoldFont = Typeface.CreateFromAsset (Forms.Context.Assets, "SourceSansPro-Semibold.otf");
}
}

Resources