ActivityIndicator in Xamarin.Forms - xamarin

Currently I am working on a project in which I have to navigate from one page (page1) to another (page2). As page2 is taking a long time to load so I was thinking to use ActivityIndicator, but it is not effective at all.
Please give me complete code to resolve my problem.
Page(1)
//code for tapping a label and to navigate to page2
void onpage1tapped(Object sender,EventArgs args)
{ label1.Opacity=0.5;
Device.StartTimer(TimeSpan.FromMilliseconds(100),()=>{label1.Opacity=1;return false; })
This.Navigate.PushAsync(new Page2());
}
Page(2)
//constructor for page2
public page2
{ InitializeComponent();
ActivityIndicator indicator=new ActivityIndicator{HorizontalOptions=LayoutOptions.CenterAndExpand,Color=Color.Black,IsVisible=false};
StackLayout stk=new StackLayout();
stk.Children.Add(indicator);
//Enabling activityindicator
indicator.IsRunning=true;
indicator.IsVisible=true;
//Below long time taking task
Assembly assembly = GetType().GetTypeInfo().Assembly;
string resource = String.Format("ks.texts.BST.txt");
//string resource = "advjava.texts.insertion.txt";
using (Stream s = assembly.GetManifestResourceStream(resource))
{
using (StreamReader sr = new StreamReader(s))
{
string line;
while (null != (line = sr.ReadLine()))
{
Label l = new Label { Text = line, TextColor = Color.FromRgb(110, 139, 69), FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) };
stk.Children.Add(l);
}
}
}
//Disabling Activity indicator as the long task is complete
indicator.IsVisible=false;
indicator.IsRunning=false;
}

Use Acr.XamForms.UserDialogs Package. Have a look at it.. Hope it helps
Here is a documentation on how to implement Acr.XamForms.UserDialogs..
Hope it helps

This is only a sample code, But it's logic is right and pretty good works for me. :)
var indicator = new ActivityIndicator() {
HorizontalOptions = LayoutOptions.CenterAndExpand
};
indicator.SetBinding(ActivityIndicator.IsVisibleProperty, "IsBusy", BindingMode.OneWay);
indicator.SetBinding(ActivityIndicator.IsRunningProperty, "IsBusy", BindingMode.OneWay);
var button = new Button() {
HorizontalOptions = LayoutOptions.Fill,
Text = "BUTTON"
};
button.Clicked += (sender, e) => IsBusy = !IsBusy;
var root = new StackLayout() {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = {
button,
indicator
}
};
Content = root;
You can refer this Link, which give a clear note on Github by ChaseFlorell.
Working Sample : Github

The Issue is with how your code is being read. It'll finish, then execute, but if you utilize the Task.Run, you'll be able to run on separate threads and keep that activity indicator up and running to show that your page is loading and your machine is busy.
void onpage1tapped(Object sender,EventArgs args) {
activityIndicator.IsVisible= true;
Task.Run( () => {
label1.Opacity=0.5;
Device.StartTimer(TimeSpan.FromMilliseconds(100),()= {
label1.Opacity=1;returnfalse;
})
This.Navigate.PushAsync(new Page2());
});
}

Related

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

Image overtaking entire screen?

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.

PulltoRefresh + await FadeTo+TranslateTo animation combination Crash on Xamarin.forms

I'm making app with using Xamarin.forms PCL.
I'm having really hard time to solve this issue.
I've spent several days but couldn't solve.
I'm using this nuget
PullToRefresh
https://github.com/jamesmontemagno/Xamarin.Forms-PullToRefreshLayout
xabre ble plugin
https://github.com/xabre/xamarin-bluetooth-le
When I use this at the same time AND Add some animation on the page.
It gives this runtime exception when I try to refresh. (It happens very rare like 1 time on 10 times)
The Crash happens ONLY iOS.
It's very rare. You can try to refresh more than 10 times. You will see.
It happens only actual iOS Device.
Foundation.MonoTouchException: Objective-C exception thrown. Name:
NSGenericException Reason: *** Collection <__NSSetM: 0x14feb8b0> was
mutated while being enumerated.
I know well what this is and when this happen.
It happens when I try to access deleted item on list or different thread.
So I made very simple source code to look this issue simply.
I don't have any list or array on my code.
Well, it happens again.
https://github.com/myallb/test_pulltorefresh
This is my sample source code for reproducing this issue. If you can help me, please look this code.
The Crash happens ONLY iOS.
It's very rare. You can try to refresh more than 10 times. You will see.
It happens only actual iOS Device.
Thanks so much.
Full source code
using Xamarin.Forms;
using Plugin.BLE.Abstractions.Contracts;
using Plugin.BLE;
using Plugin.BLE.Abstractions.EventArgs;
using System;
using System.Diagnostics;
using Refractored.XamForms.PullToRefresh;
using System.Threading.Tasks;
namespace test
{
public partial class testPage : ContentPage
{
public static IAdapter Adapter { set; get; }
public PullToRefreshLayout RefreshView = null;
AbsoluteLayout layout;
public testPage()
{
InitializeComponent();
Adapter = CrossBluetoothLE.Current.Adapter;
if (Adapter != null)
{
Adapter.DeviceAdvertised += OnEvent_DeviceAdvertised;
Adapter.DeviceConnected += OnEvent_DeviceConnected;
Adapter.DeviceConnectionLost += OnEvent_DeviceConnectionLost;
Adapter.DeviceDisconnected += OnEvent_DeviceDisconnected;
Adapter.DeviceDiscovered += OnEvent_DeviceDiscovered;
Device.StartTimer(TimeSpan.FromSeconds(5), Timer_ScanDevice);
}
else {
}
layout = new AbsoluteLayout()
{
BackgroundColor = Color.Purple,
};
ScrollView scrollview = new ScrollView()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = layout
};
RefreshView = new PullToRefreshLayout
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = scrollview,
RefreshColor = Color.Red,
RefreshCommand = new Command(RefreshStart)
};
RefreshView.IsPullToRefreshEnabled = true;
Content = RefreshView;
Device.StartTimer(new TimeSpan(0, 0, 1), ani);
}
bool ani()
{
Label z = new Label()
{
Text = "Z",
TextColor = Color.White,
FontAttributes = FontAttributes.Bold,
FontSize = new Random().Next(22, 35)
};
AbsoluteLayout.SetLayoutBounds(z, new Rectangle(0.67 + new Random().Next(0, 10) / 100.0, 0.13 + new Random().Next(0, 10) / 100.0, 40, 40));
AbsoluteLayout.SetLayoutFlags(z, AbsoluteLayoutFlags.PositionProportional);
layout.Children.Add(z);
Device.BeginInvokeOnMainThread(async () =>
{
Task t1 = z.FadeTo(0, 3500);
Task t2 = z.TranslateTo(0, -70, 3500, Easing.SinInOut);
await Task.WhenAll(t1, t2);
layout.Children.Remove(z);
});
return true;
}
void RefreshStart()
{
Debug.WriteLine("RefreshStart");
if (RefreshView != null)
RefreshView.IsRefreshing = true;
Device.BeginInvokeOnMainThread(async () =>
{
await Task.Delay(20);
Debug.WriteLine("RefreshEnd");
RefreshView.IsRefreshing = false;
});
}
bool Timer_ScanDevice()
{
Adapter.StartScanningForDevicesAsync();
return true;
}
void OnEvent_DeviceAdvertised(object sender, DeviceEventArgs a)
{
Debug.WriteLine("OnEvent_DeviceAdvertised");
}
void OnEvent_DeviceDiscovered(object sender, DeviceEventArgs a)
{
Debug.WriteLine("OnEvent_DeviceDiscovered");
}
void OnEvent_DeviceConnected(object sender, DeviceEventArgs a)
{
Debug.WriteLine("OnEvent_DeviceConnected");
}
void OnDeviceProcessError(IDevice device, string message)
{
Debug.WriteLine("OnDeviceProcessError");
}
void OnEvent_DeviceConnectionLost(object sender, DeviceErrorEventArgs a)
{
Debug.WriteLine("OnEvent_DeviceConnectionLost");
}
void OnEvent_DeviceDisconnected(object sender, DeviceEventArgs a)
{
Debug.WriteLine("OnEvent_DeviceDisconnected");
}
}
}

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");
}
}

Expanding the size of ListView cell on tap

I'm trying to implement a solution to increase the size of a ListView Cell when tapped using Xamarin Forms (and custom renderers if required).
I'm still pretty new to C#, and the idea of data binding is still a little unclear to me, however, it seems like that is the way to go to solve this problem (perhaps something along the lines of binding the Height / HeightRequest properties of the cell?).
My attempts thus far have been unsuccessful.
If anyone could give me a push in the right direction it would be much appreciated.
Thank you!
ViewCell does not expose Height as a BindableProperty in Xamarin.Forms 1.4.2x
However if you create your own BindableProperty in your Model you can achieve changing the height still as shown below:-
Model:-
public class MenuItem2 : BindableObject
{
public static readonly BindableProperty TextProperty = BindableProperty.Create<MenuItem2, string>(p => p.Text, default(string));
public static readonly BindableProperty CellHeightProperty = BindableProperty.Create<MenuItem2, int>(p => p.CellHeight, default(int));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public int CellHeight
{
get { return (int)GetValue(CellHeightProperty); }
set { SetValue(CellHeightProperty, value); }
}
}
XAML:-
<StackLayout>
<Button x:Name="cmdButton1" Text="Change Cell Heights" Clicked="cmdButton1_Clicked"/>
<ListView x:Name="lstItems" />
</StackLayout>
XAML Code-Behind:-
lstItems.HasUnevenRows = true;
lstItems.ItemTemplate = new DataTemplate(typeof(Classes.MenuCell2));
//
lstItems.ItemsSource = new List<MenuItem2>
{
new MenuItem2(),
new MenuItem2(),
new MenuItem2(),
new MenuItem2(),
};
If you don't set .HasUnevenRows you will not be able to change the cell height.
void cmdButton1_Clicked(object sender, EventArgs e)
{
Random objRandom = new Random();
//
var objItems = lstItems.ItemsSource;
//
foreach (MenuItem2 objMenuItem in objItems)
{
int intNewCellHeight = objRandom.Next(80, 160);
objMenuItem.CellHeight = intNewCellHeight;
objMenuItem.Text = "Cell Height = " + intNewCellHeight.ToString();
}
}
Custom ViewCell:-
public class MenuCell2 : ViewCell
{
public MenuCell2()
{
Label objLabel = new Label
{
YAlign = TextAlignment.Center,
TextColor = Color.Yellow,
};
objLabel.SetBinding(Label.TextProperty, new Binding("Text"));
StackLayout objLayout = new StackLayout
{
Padding = new Thickness(20, 0, 0, 0),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.StartAndExpand,
Children = { objLabel }
};
Frame objFrame_Inner = new Frame
{
Padding = new Thickness(15, 15, 15, 15),
HeightRequest = 36,
OutlineColor = Color.Accent,
BackgroundColor = Color.Blue,
Content = objLayout,
};
Frame objFrame_Outer = new Frame
{
Padding = new Thickness(0, 0, 0, 10),
Content = objFrame_Inner
};
View = objFrame_Outer;
this.BindingContextChanged += MenuCell2_BindingContextChanged;
}
void MenuCell2_BindingContextChanged(object sender, EventArgs e)
{
MenuItem2 objMenuItem = (MenuItem2)this.BindingContext;
objMenuItem.PropertyChanged += objMenuItem_PropertyChanged;
}
void objMenuItem_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "CellHeight":
this.Height = (this.BindingContext as MenuItem2).CellHeight;
(this.View as Frame).ForceLayout();
break;
}
}
Remember to call ForceLayout on the root element of the ViewCell's View property, so it can redraw correctly.
This will give you a result something similar to the following (tested only on WindowsPhone at present):-
In order to do it on a ViewCell being tapped, on the XAML Page add:-
lstItems.ItemTapped += lstItems_ItemTapped;
and then change the model for the item to something like this:-
void lstItems_ItemTapped(object sender, ItemTappedEventArgs e)
{
(e.Item as MenuItem2).CellHeight = 200;
}
Xamarin now has an official example of doing this right within xaml and xaml code behind:
Overview:
https://developer.xamarin.com/samples/xamarin-forms/UserInterface/ListView/DynamicUnevenListCells/
Code:
https://github.com/xamarin/xamarin-forms-samples/tree/master/UserInterface/ListView/DynamicUnevenListCells

Resources