I have this annoying problem and I don't know how to solve this.
In Xamarin Forms, I'm trying to draw a dynamic layout, for this I load a list of elements (this works). Now i'm trying to display the label for it, so I loop through all the items and add a label for every item. The problem is that the page stays empty.
Yes I initialized the _layout variable as a StackLayout and I also made a ScrollView, then I set the scrollview's content to the _layout variable. But still my page stays empty. I can't share the actual code but I rewrote it using different names.
private void DrawItems()
{
var items = (List<Item>)_database.GetItems();
foreach(var item in items)
{
DrawItem(item);
}
}
private void DrawItem(Item item)
{
AddLabel(item);
}
private void AddLabel(Item item)
{
if (string.IsNullOrEmpty(item.Text)) return;
var label = new Label
{
Text = (!string.IsNullOrEmpty(item.Number)) ? string.Format("{0}: {1}", item.Number, item.Text) : item.Text,
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label))
};
_layout.Children.Add(label);
}
For some weird reason, when I start debugging (put a break on var label ...) the label get's created but when I put a break on _layout.Children.Add(label), this never gets called.
When changing UI elements, you need to do it on the main thread.
Try this:
Device.BeginInvokeOnMainThread(() =>
{
_layout.Children.Add(label);
}
I've tried the following code, and it works for me. I'm using Xamarin.Forms 1.4.0.6341
public class MyPage : ContentPage
{
StackLayout stack;
public MyPage()
{
var scroll = new ScrollView();
stack = new StackLayout();
var btn = new Button {Text = "Add Label"};
btn.Clicked += (sender, args) => stack.Children.Add(new Label {Text = "Test"});
stack.Children.Add(btn);
AddLabelEverySecond();
scroll.Content = stack;
Content = scroll;
}
private async void AddLabelEverySecond()
{
for (var i = 0; i < 10; i++)
{
await Task.Delay(1000);
stack.Children.Add(new Label { Text = "1 second" });
}
}
}
Is this applicable to your code? Maybe you could tell us where DrawItems is called from?
Related
Good Afternoon,
I have a question regarding the performance of the tabbed page and whether there is a more efficient way to load the tabs into the page, without any frame loss or process hang.
Regarding this issue, I have been having a few issues with TabbedPage in Xamarin.Forms. My project currently consists of a Listview with 104 components. When I click on one of the items it opens up a tabbed page consisting of 3 tabed pages. I start the tabbed page using the following code
int Clicked = 0;
public async Task CheckClick(Page data)
{
Clicked += 1;
if (Clicked == 1)
{
await Navigation.PushAsync(data);
List_View.SelectedItem = null;
}
}
public async void OnSelection(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
{
return;
}
var Cell = e.SelectedItem as DataSource;
switch (Cell.ID)
{
case 0:
await CheckClick(new Function_One());
break;
}
}
The real problem comes after, whether I pre load the information ahead of time or do what ever, whenever I call Children.Add(data); the whole application hangs for 1 or 2 seconds and than allows the page to load. The code is as follows.
public class Function_One : TabbedPage
{
private async Task Test()
{
//
var data = new NewPageData("", "", "OP.png", "Persian.png", Description, Usage, Storage, Data);
var data2 = new NewMedicalPage(Medical_Info, Translation, startinfo);
var data3 = new NewNotePage("", 0);
data.Icon = "Info.png";
await Task.Delay(100);// Little await to load the page first before hang
Device.BeginInvokeOnMainThread(async () =
{
Children.Add(data);// HERE is the freeze
Children.Add(data2);// these
Children.Add(data3);// 3 (Adding of the Tabs)
});
}
public void UpdateData()
{
Task.Run(async () =
{
try
{
Description.Spans.Add(new Span
{
Text = "Data.... ",
FontSize = 18,
FontFamily = variables.fontFamily
});
Usage.Spans.Add(new Span
{
Text = "MoreData...",
FontSize = 18,
FontFamily = variables.fontFamily
});
Storage.Spans.Add(new Span
{
Text = "LastData...",
FontSize = 18,
FontFamily = variables.fontFamily
});
await Test();
}
catch { }
}).ConfigureAwait(false);
}
public Function_One()
{
UpdateData();
BarBackgroundColor = MainColor;
BarTextColor = Color.WhiteSmoke;
Title = "Page";
}
}
NOTE: (This Project Is Completely An Offline Project, No Internet Needed)
If anyone could explain to me a better method for loading the data while keeping the application flowing and smooth, that would be appreciated. The data does not have to load all at once, just as long as the page opens as soon as its clicked!
Am trying to load ItemSource of a picker when the picker is focused.
But the data is not loaded on 1st focus.
here is the code sample
List<object> itmSrc;
Picker picker = new Picker();
itmSrc = Controls[i].ItemSource;
picker.Focused += BindItemSourceOnFocus;
public void BindItemSourceOnFocus(object sender, FocusEventArgs e)
{
var p = e.VisualElement as Picker;
p.ItemsSource = itmSrc;
}
If any other different approach is possible, let me know.
You can do it adding items on an async method, or another thread. Load the data on view focus is just transferring the issue to another place, and it gives a bad user experience at all.
If you run a code block inside a Task.Run(), for example, this code will be executed on another thread, and the interface should not hang on data loading.
Something like this:
public class MyPage : ContentPage
{
List<object> itmSrc;
Picker picker;
public MyPage()
{
// Your stuff goes here
itmSrc = new List<object>();
picker = new Picker();
StackLayout content = new StackLayout();
content.Crindren.Add(picker);
this.Content = content;
Task.Run(() => LoadData());
}
private void LoadData()
{
// Get your data from anywhere and put it on the itemSrc from here.
// Then...
picker.ItemsSource = itmSrc;
}
}
I hope it helps.
(Complete code example is available here:
https://github.com/csvan/TabbedPageModalDemo)
I have the following page structure in my Xamarin.Forms app:
namespace TabbedPageModalDemo
{
public class MyTabbedPage : TabbedPage
{
public MyTabbedPage ()
{
Title = "Tabbed Page";
Children.Add (new MyTabPage ());
}
}
}
namespace TabbedPageModalDemo
{
public class MyTabPage : ContentPage
{
public MyTabPage ()
{
Title = "Tab Page";
var button = new Button {
Text = "Open Modal"
};
// Using PushModal as an example - PushAsync also fills the whole screen.
button.Clicked += (sender, e) => Navigation.PushModalAsync (new MyModalPage ());
var stack = new StackLayout ();
stack.Children.Add (button);
Content = stack;
}
}
}
namespace TabbedPageModalDemo
{
public class MyModalPage : ContentPage
{
public MyModalPage ()
{
Title = "Modal Page";
var label = new Label {
Text = "I am a modal page!"
};
var stack = new StackLayout ();
stack.Children.Add (label);
Content = stack;
}
}
}
What I want is the following: when I press the button, I would like MyModalPage to only take up the same space that is occupied by MyTabPage. Currently, it fills the entire screen, covering MyTabbedPage as well. Note that the same happens if I use PushAsync instead.
How can I resolve this?
Did you try used padding inside your current modal page?
Padding = new Thickness(0,30,0,0);
I think the better approach would be to use NavigationPages instead of a ContentPage as your Child-Elements. A good example can be seen here.
I would like to create a layout with a fullscreen background image and some UI elements on top of it. The twist is this:
I would like the background image to swipeable like a carousel, but I would like the UI elements to stay in place. That is if I swipe the screen, the background image should slide to the side and a new image should replace it. I know about CarouselPage, but it seems to me that it won't do the trick, since a Page can have only one child which it replaces on swipe, meaning that the UI elements would be descendants of the CarouselPage and therefore would also be animated.
I am guessing I need some sort of custom renderer here, but how should I go about designing it? Should it be one fullscreen Image control replaced be another fullscreen Image control with the UI elements on top of it? And how can I do this? Or is there an all together better approach?
I am developing for iOS and Android using Xamarin.Forms.
Thanks in advance.
I don't like repeating myself much, and I think that multiple layers of actionable items can lead to confusion, but the problems appeals to me and I can see a niche for this kind of UI, so here's my take on your question.
Let's assume this is the (Xamarin.Forms.)Page you want to render with a custom carousel background:
public class FunkyPage : ContentPage
{
public IList<string> ImagePaths { get; set; }
public FunkyPage ()
{
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
Spacing = 12,
Children = {
new Label { Text = "Foo" },
new Label { Text = "Bar" },
new Label { Text = "Baz" },
new Label { Text = "Qux" },
}
};
ImagePaths = new List<string> { "red.png", "green.png", "blue.png", "orange.png" };
}
}
The renderer for iOS could look like this:
[assembly: ExportRenderer (typeof (FunkyPage), typeof (FunkyPageRenderer))]
public class FunkyPageRenderer : PageRenderer
{
UIScrollView bgCarousel = new UIScrollView (RectangleF.Empty) {
PagingEnabled = true,
ScrollEnabled=true
};
List<UIImageView> uiimages = new List<UIImageView> ();
protected override void OnElementChanged (VisualElementChangedEventArgs e)
{
foreach (var sub in uiimages)
sub.RemoveFromSuperview ();
uiimages.Clear ();
if (e.NewElement != null) {
var page = e.NewElement as FunkyPage;
foreach (var image in page.ImagePaths) {
var uiimage = new UIImageView (new UIImage (image));
bgCarousel.Add (uiimage);
uiimages.Add (uiimage);
}
}
base.OnElementChanged (e);
}
public override void ViewDidLoad ()
{
Add (bgCarousel);
base.ViewDidLoad ();
}
public override void ViewWillLayoutSubviews ()
{
base.ViewWillLayoutSubviews ();
bgCarousel.Frame = View.Frame;
var origin = 0f;
foreach (var image in uiimages) {
image.Frame = new RectangleF (origin, 0, View.Frame.Width, View.Frame.Height);
origin += View.Frame.Width;
}
bgCarousel.ContentSize = new SizeF (origin, View.Frame.Height);
}
}
This was tested and works. Adding a UIPageControl (the dots) is easy on top of this. Autoscrolling of the background is trivial too.
The process is similar on Android, the overrides are a bit different.
How do I create an a slider menu using Xamarin.Forms? Is it baked in or something custom?
You create a new class which contains all the definitions for both the Master - i.e. the menu - and the Detail - i.e. the main page. I know, it sounds back-to-front, but for example..
using System;
using Xamarin.Forms;
namespace testXamForms
{
public class HomePage : MasterDetailPage
{
public HomePage()
{
// Set up the Master, i.e. the Menu
Label header = new Label
{
Text = "MENU",
Font = Font.BoldSystemFontOfSize(20),
HorizontalOptions = LayoutOptions.Center
};
// create an array of the Page names
string[] myPageNames = {
“Main”,
“Page 2”,
“Page 3”,
};
// Create ListView for the Master page.
ListView listView = new ListView
{
ItemsSource = myPageNames,
};
// The Master page is actually the Menu page for us
this.Master = new ContentPage
{
Title = "The Title is required.",
Content = new StackLayout
{
Children =
{
header,
listView
},
}
};
// Define a selected handler for the ListView contained in the Master (ie Menu) Page.
listView.ItemSelected += (sender, args) =>
{
// Set the BindingContext of the detail page.
this.Detail.BindingContext = args.SelectedItem;
Console.WriteLine("The args.SelectedItem is
{0}",args.SelectedItem);
// This is where you would put your “go to one of the selected pages”
// Show the detail page.
this.IsPresented = false;
};
// Set up the Detail, i.e the Home or Main page.
Label myHomeHeader = new Label
{
Text = "Home Page",
HorizontalOptions = LayoutOptions.Center
};
string[] homePageItems = { “Alpha”, “Beta”, “Gamma” };
ListView myHomeView = new ListView {
ItemsSource = homePageItems,
};
var myHomePage = new ContentPage();
myHomePage.Content = new StackLayout
{
Children =
{
myHomeHeader,
myHomeView
} ,
};
this.Detail = myHomePage;
}
}
}
It is built in: MasterDetailPage. You'd set the Detail and Master properties of it to whatever kinds of Pages you'd like. I found Hansleman.Forms to be quite enlightening.
My minimum example (as posted here) is as follows:
public class App
{
static MasterDetailPage MDPage;
public static Page GetMainPage()
{
return MDPage = new MasterDetailPage {
Master = new ContentPage {
Title = "Master",
BackgroundColor = Color.Silver,
Icon = Device.OS == TargetPlatform.iOS ? "menu.png" : null,
Content = new StackLayout {
Padding = new Thickness(5, 50),
Children = { Link("A"), Link("B"), Link("C") }
},
},
Detail = new NavigationPage(new ContentPage {
Title = "A",
Content = new Label { Text = "A" }
}),
};
}
static Button Link(string name)
{
var button = new Button {
Text = name,
BackgroundColor = Color.FromRgb(0.9, 0.9, 0.9)
};
button.Clicked += delegate {
MDPage.Detail = new NavigationPage(new ContentPage {
Title = name,
Content = new Label { Text = name }
});
MDPage.IsPresented = false;
};
return button;
}
}
An example solution is hosted on GitHub.
On iOS the result looks like this (left: menu open, right: after clicking on "B"):
Note that you need to add the menu icon as a resource in your iOS project.
If you are looking for simple example of MasterDetailPage please have a look at my sample repo at GitHub. Very nice example is also presented here
Slideoverkit is a great plugin available for Xamarin Forms. There is a github to see free samples and you could find documentation about it here.