asynchronous operation with recyclerview and asynctask in xamarin android - c#

i just started to work on performance in mobile app development and i am using asynctask. but i encounter a few challenges, whenever i instantiate my recyclerview in the oncreate method of my activity and retrieve data through async task, i then try to update the recyclerview adapter but it doesn't work. Here's my code below. Here's my activity below
public class awarenessActivity : Activity
{
public RecyclerView mRecyclerView;
public RecyclerView.LayoutManager mLayoutManager;
public RecyclerAdapter mAdapter;
public List<AwarePosts> awrPosts;
ScrollView tabsitem;
ImageButton aware;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//preparing the datasource
awrPosts = new List<AwarePosts>();//empty for now, its the data that i'm trying to retrieve with asynctask
//setting view from Layout resource
SetContentView(Resource.Layout.Main);
//get the linearlayout from the layout resource
tabsitem = (ScrollView)FindViewById(Resource.Id.scrollView1);
tabsitem.RemoveAllViews();
LayoutInflater.Inflate(Resource.Layout.awareness, tabsitem);
//this adds this layout to the main page
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
//Create our layoutManager
mLayoutManager = new StaggeredGridLayoutManager(2, 1);
mRecyclerView.SetLayoutManager(mLayoutManager);
mRecyclerView.NestedScrollingEnabled = false;
//instantiate the adapter and pass in it the datasource
mAdapter = new RecyclerAdapter(awrPosts);
mAdapter.ItemClick += OnItemClick;
//plug the adapter into the recycler view
mRecyclerView.SetAdapter(mAdapter);
new retrievePosts(this).Execute();//executing the asynctask
ActionBar.SetHomeButtonEnabled(true);
ActionBar.SetDisplayHomeAsUpEnabled(true);// these are for the back arrow item
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == global::Android.Resource.Id.Home)
{
StartActivity(typeof(menuActivity));
return true;
}
return base.OnOptionsItemSelected(item);
}
public void OnItemClick(object sender, int position)
{
string img, author, caption, info, postid, link, videolink;
try
{
img = awrPosts[position].image;
author = awrPosts[position].author;
caption = awrPosts[position].title;
info = awrPosts[position].externaL_LINK;
postid = awrPosts[position].awarenesS_EVENT_ID.ToString();
link = awrPosts[position].externaL_LINK;
videolink = awrPosts[position].videO_LINK;
string[] arr = new string[] { img.ToString(), author, caption, info, postid, link, videolink };
Bundle b = new Bundle();
Intent awareDetailInt = new Intent(this, typeof(awareDetailActivity));
b.PutStringArray("array", arr);
awareDetailInt.PutExtras(b);
StartActivity(awareDetailInt);
}
catch (Exception ex)
{
Toast.MakeText(this, ex.Message, ToastLength.Long).Show();
}
}
public override void OnBackPressed()
{
Finish();
}
internal List<AwarePosts> getPosts(List<string> imag)
{
List<AwarePosts> ap = new List<AwarePosts>();
try
{
ap.Add(new AwarePosts()
{
awarenesS_EVENT_ID = 1,
gendeR_TARGET = 2,
image = imag.ElementAt(0),
title = "Are you Happy?",
author = "casual optimist",
videO_LINK = "",
externaL_LINK = "",
agE_TARGET_MIN = 5,
agE_TARGET_MAX = 9,
creatioN_DATE = "",
creatioN_USER = ""
});
ap.Add(new AwarePosts()
{
awarenesS_EVENT_ID = 2,
gendeR_TARGET = 2,
image = imag.ElementAt(1),
title = "Positive Vibes",
author = "etsy",
videO_LINK = "",
externaL_LINK = "",
agE_TARGET_MIN = 5,
agE_TARGET_MAX = 9,
creatioN_DATE = "",
creatioN_USER = ""
});
ap.Add(new AwarePosts()
{
awarenesS_EVENT_ID = 3,
gendeR_TARGET = 2,
image = imag.ElementAt(2),
title = "30+ funny comics",
author = "jimmy benton",
videO_LINK = "",
externaL_LINK = "",
agE_TARGET_MIN = 5,
agE_TARGET_MAX = 9,
creatioN_DATE = "",
creatioN_USER = ""
});
ap.Add(new AwarePosts()
{
awarenesS_EVENT_ID = 4,
gendeR_TARGET = 2,
image = imag.ElementAt(3),
title = "Smiling Doodle",
author = "instagram",
videO_LINK = "",
externaL_LINK = "",
agE_TARGET_MIN = 5,
agE_TARGET_MAX = 9,
creatioN_DATE = "",
creatioN_USER = ""
});
ap.Add(new AwarePosts()
{
awarenesS_EVENT_ID = 5,
gendeR_TARGET = 2,
image = imag.ElementAt(4),
title = "Have a good day",
author = "society6",
videO_LINK = "",
externaL_LINK = "",
agE_TARGET_MIN = 5,
agE_TARGET_MAX = 9,
creatioN_DATE = "",
creatioN_USER = ""
});
return ap;
}
catch (WebException web)
{
ap.Add(new AwarePosts { title = web.Response.ToString() });
return ap;
}
catch (Exception ex)
{
ap.Add(new AwarePosts { title = ex.Message + "\n" + ex.StackTrace});
return ap;
}
}
public List<string> drawableImageToBase64String(Context context)//doing some local data collection here
{
List<string> images = new List<string>();
int[] imgs = new int[] { Resource.Drawable.first1, Resource.Drawable.second1, Resource.Drawable.third1, Resource.Drawable.fourth1, Resource.Drawable.fifth2 };
foreach(var item in imgs)
{
Bitmap bitmap = BitmapFactory.DecodeResource(context.Resources, item);
MemoryStream stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
byte[] ba = stream.ToArray();
string bal = Convert.ToBase64String(ba);
images.Add(bal);
}
return images;
}
}
here's my edited asynctask code
public class retrievePosts : AsyncTask<Java.Lang.Void, Java.Lang.Void, List<AwarePosts>>
{
awarenessActivity aw;
List<AwarePosts> awr;
Context mContext;
RecyclerAdapter mAdapter;
public retrievePosts(awarenessActivity awre, Context context, RecyclerAdapter adapter)
{
aw = awre;
mContext = context;
mAdapter = adapter;
}
protected override void OnPreExecute()
{
AndroidHUD.AndHUD.Shared.ShowImage(mContext, Resource.Drawable.load2, "Getting Posts...");
}
protected override List<AwarePosts> RunInBackground(Java.Lang.Void[] #params)
{
List<string> img = aw.drawableImageToBase64String(mContext);
awr = aw.getPosts(img);
return awr;
}
protected override void OnPostExecute(List<AwarePosts> result)
{
base.OnPostExecute(result);
mAdapter = new RecyclerAdapter(result);
mAdapter.NotifyDataSetChanged();
AndroidHUD.AndHUD.Shared.Dismiss(mContext);
Toast.MakeText(mContext, "successful", ToastLength.Long).Show();
}
}
and i'm calling it with this
awarenessActivity aw;
ScrollView tabsitem;
ImageButton aware;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//preparing the datasource
aw = new awarenessActivity();
awrPosts = new List<AwarePosts>();
//set our view from the "Playlist" Layout resource
SetContentView(Resource.Layout.Main);
//get the linearlayout from the layout resource
tabsitem = (ScrollView)FindViewById(Resource.Id.scrollView1);
tabsitem.RemoveAllViews();
LayoutInflater.Inflate(Resource.Layout.awareness, tabsitem);
//this adds this layout to the main page
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
//Create our layoutManager
mLayoutManager = new StaggeredGridLayoutManager(2, 1);
mRecyclerView.SetLayoutManager(mLayoutManager);
mRecyclerView.NestedScrollingEnabled = false;
//instantiate the adapter and pass in it the datasource
mAdapter = new RecyclerAdapter(awrPosts);
mAdapter.ItemClick += OnItemClick;
//plug the adapter into the recycler view
mRecyclerView.SetAdapter(mAdapter);
new retrievePosts(aw, this, mAdapter).Execute();}

You did wrong to new a awarenessActivity in your retrievePosts, you can pass the mAdapter as parameter to your retrievePosts, try to modify your retrievePosts like this:
public class retrievePosts : AsyncTask<string, string, List<AwarePosts>>
{
List<AwarePosts> awr;
Context mContext;
RecyclerAdapter mAdapter
public retrievePosts(Context context, RecyclerAdapter adapter)
{
mContext = context;
mAdapter = adapter;
}
protected override void OnPreExecute()
{
AndroidHUD.AndHUD.Shared.ShowImage(mContext, Resource.Drawable.load2, "Getting Posts...");
}
protected override List<AwarePosts> RunInBackground(params string[] #params)
{
List<string> img = aw.drawableImageToBase64String(mContext);
//not sure what are you doing here, if you want to get some objects from your awarenessActivity, still try to make it as a parameter and pass to here.
return awr;
}
protected override void OnPostExecute(List<AwarePosts> result)// this function is supposed to run on the UI thread
{
base.OnPostExecute(result);
mAdapter = new RecyclerAdapter(result); // assigning the data here
mAdapter.NotifyDataSetChanged();//y'all kn what i'm trying to do here
AndroidHUD.AndHUD.Shared.Dismiss(mContext);
Toast.MakeText(mContext, "successful", ToastLength.Long).Show();
}
}
And when you use it, try like this new retrievePosts(this, mAdapter).Execute();.
I didn't test this code, but it should be something like it. I noticed that in your RunInBackground method, you also tried to get some object from the awarenessActivity, try to pass it like RecyclerAdapter here as parameter to this class. Don't create a new Activity in it.

Finally, i got it to work #grace Feng's answer was correct. i just didn't set the recycler view adapter inside the onpostexecute method after mAdapter.NotifyDataSetChanged();
after adding this line, it worked and my recycler view was populated. Thanks grace feng
mRecyclerView.SetAdapter(mAdapter);

Related

Label doesn't properly update on defered event handler [Bug?]

I noted something wrong, either it's from me or a bug when experimenting with .Net MAUI.
I have an ObservableCollection property :
public ObservableCollection<LotoModel> Lotteries { get; set; } = new();
and an ObservableProperty (using community mvvm toolkit) :
[ObservableProperty]
public string _lotteriesCount = "A";
When ever I click/touch a Button I load my Lotteries collection. (tested with an hard coded 4 static LotoModel items) :
static GridModel _testGM1 = new GridModel { Name = "TEST Grid #1", Start = 0, End = 10, NumberOfDraw = 2 };
static GridModel _testGM2 = new GridModel { Name = "TEST Grid #2", Start = 0, End = 20, NumberOfDraw = 5 };
static GridModel _testGM3 = new GridModel { Name = "TEST Grid #3", Start = 0, End = 30, NumberOfDraw = 8 };
static GridModel _testGM4 = new GridModel { Name = "TEST Grid #4", Start = 0, End = 50, NumberOfDraw = 10 };
static LotoModel _testLM1 = new LotoModel { Name = "TEST Lottery #1", IsFavorite = true, Grids = new ObservableCollection<GridModel> { _testGM1, _testGM2 } };
static LotoModel _testLM2 = new LotoModel { Name = "TEST Lottery #2", IsFavorite = false, Grids = new ObservableCollection<GridModel> { _testGM3, _testGM4 } };
And the Button command Task :
async Task GetLotteriesAsync()
{
if (IsBusy)
return;
try
{
IsBusy = true;
_buttonCount++;
LotteriesCount = _buttonCount.ToString();
if (Lotteries.Count != 0)
Lotteries.Clear();
Lotteries.Add(_testLM1);
Lotteries.Add(_testLM2);
Lotteries.Add(_testLM1);
Lotteries.Add(_testLM2);
}
catch (Exception e)
{
Log.Error(e, "Error while trying to get our lotteries");
await Application.Current.MainPage.DisplayAlert("Error!", e.Message, "OK");
}
finally
{
IsBusy = false;
}
}
So each time I touch/click my Button, LotteriesCount string property get's updated with a static int counter field value :
static int _buttonCount = 0;
That is OK.
Now I also update this property via this CollectionChangedEventHandler :
public LotteriesVM(LotteryService lotteryService)
{
GetLotteriesCommand = new Command(async () => await GetLotteriesAsync());
Lotteries.CollectionChanged += LotteriesChangedMethod;
}
private void LotteriesChangedMethod(object sender, NotifyCollectionChangedEventArgs e)
{
LotteriesCount = _lotteriesCount + "_" + Lotteries.Count.ToString();
}
And now here the unexpected behavior : The label only update the counter part of it string property, the remaining "_1_2_3_4" added in the handler doesn't get updated in UI.
Note that I'm using a Android Pixel 5 emulator from VS 17.3 preview.
And I also noted that if I force orientation of the emulated android device, then the Label is updated !
Even more, if I force a XAML MinimalWidthRequest="200" for my Label, then it is correctly updated now !
<!-- without MinimumWidthRequest, the label update only after forcing a screen orientation-->
<Label Text="{Binding LotteriesCount}" MinimumWidthRequest="200" HorizontalOptions="Center" FontAttributes="Bold" FontSize="22" TextColor="OrangeRed"/>
So am I doing something wrong or it is a bug ? Thank you.

Android.Util.AndroidRuntimeException: <Timeout exceeded getting exception details> occurred

I am android c# developer beginner and need to work in back ground showing my listview but my code have a load action such as fetching database and convert from byte array to image so I used async task but it hangs and give me this error Unhandled Exception:
Android.Util.AndroidRuntimeException: occurred
//this my async class :
public class UpdatePB : AsyncTask<string, string, string>
{
private Activity context;
private ProgressDialog progressDialog;
public List<Advertise_TableItem> GroupLst;
Dictionary<Advertise_TableItem, List<Replies_TableItem>> ChildLst;
// Advertise_HomeScreenAdapter adb;
public ExpandableListViewAdapter Adapter;
AbdoService.abdo_service AbdoService;
//string user_id;
Context myContext;
ExpandableListView listview;
DataTable table;
public UpdatePB(Activity context, ExpandableListView listview, Context myContext)
{
this.context = context;
this.listview = listview;
this.myContext = myContext;
}
protected override string RunInBackground(string[] #params)
{
//System.Threading.Thread.Sleep(3000);
//adb = new Advertise_HomeScreenAdapter(this, tableItems);
//listview.Adapter = adb;
//progressDialog = new ProgressDialog(context);
//progressDialog.Show();
AbdoService = new AbdoService.abdo_service();
GroupLst = new List<Advertise_TableItem>();
ChildLst = new Dictionary<Advertise_TableItem, List<Replies_TableItem>>();
if (CrossConnectivity.Current.IsConnected)
{
table = AbdoService.selectAllAdvertises().table;
int I = 0;
foreach (DataRow item in table.Rows)
{
// bitmap = BitmapFactory.DecodeByteArray(AbdoService.GetByteImage(item[1].ToString()).ByteImage, 0, AbdoService.GetByteImage(item[1].ToString()).ByteImage.Length);
GroupLst.Add(new Advertise_TableItem(item[1].ToString(), item[2].ToString(), item[3].ToString(), item[0].ToString()));
List<Replies_TableItem> child = new List<Replies_TableItem>();
//child.Add(new Replies_TableItem("mohammed", "comment 1 "));
//child.Add(new Replies_TableItem("ahmed", "comment 2 "));
var childTable = AbdoService.selectReply(item[0].ToString());
foreach (DataRow childItem in childTable.table.Rows)
{
child.Add(new Replies_TableItem(childItem[1].ToString(), childItem[2].ToString()));
}
ChildLst.Add(GroupLst[I], child);
I++;
}
}
else
{
if_connected();
}
Adapter = new ExpandableListViewAdapter(myContext, GroupLst, ChildLst);
////System.Threading.Thread.Sleep(3000);
listview.SetAdapter(Adapter);
for (int i = 0; i < Adapter.GroupCount; i++)
{
listview.ExpandGroup(i);
}
//listview.GroupClick += Listview_GroupClick;
//startServices();
////RunOnUiThread(() => progressBar.Visibility = ViewStates.Invisible);
return context.Title;
}
public void if_connected()
{
var callDialog = new Android.App.AlertDialog.Builder(context);
callDialog.SetTitle("Notify");
callDialog.SetMessage("connect internet");
callDialog.SetNeutralButton("ok", delegate
{
context.Finish();
context.StartActivity(typeof(AllAdvertises));
});
callDialog.Show();
}
protected override void OnPreExecute()
{
progressDialog = new ProgressDialog(context);
progressDialog.Show();
}
//protected override void OnProgressUpdate(params string[] values)
//{
// //mtv.Text = Convert.ToString(values[0]);
// //Android.Util.Log.Error("lv==", values[0] + "");
// listview.SetAdapter(Adapter);
// for (int i = 0; i < Adapter.GroupCount; i++)
// {
// listview.ExpandGroup(i);
// }
//}
protected override void OnPostExecute(string result)
{
result = context.Title;
progressDialog.Dismiss();
}
}
//this is my Activity function oncreate()
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetTheme(Resource.Style.AppTheme);
SetContentView(Resource.Layout.AllAdvertises);
//progressDialog = new ProgressDialog(this);
//progressDialog.SetMessage("hiiiiiiiiiiiii");
//progressDialog.Show();
// System.Threading.Thread.Sleep(1000);
//ProgressBar progressBar = FindViewById<ProgressBar>(Resource.Id.progressBar);
//RunOnUiThread(() => progressBar.Visibility=ViewStates.Visible);
//DoSomeWork1(arguments);
prefs = PreferenceManager.GetDefaultSharedPreferences(this);
TextView UserName = FindViewById<TextView>(Resource.Id.txtUserName);
string user_id = prefs.GetString("UserId", "0");
if (user_id != "0")
{
UserName.Text = " Hello " + prefs.GetString("UserName", "0");
}
//AbdoService = new AbdoService.abdo_service();
////table= b.GetResult();
//// //AbdoService.selectAllAdvertisesCompleted += AbdoService_selectAllAdvertisesCompleted;
//// //AbdoService.selectAllAdvertisesAsync();
listview = FindViewById<ExpandableListView>(Resource.Id.EdListView);
//GroupLst = new List<Advertise_TableItem>();
//ChildLst = new Dictionary<Advertise_TableItem, List<Replies_TableItem>>();
//if (CrossConnectivity.Current.IsConnected)
//{
// //table = AbdoService.selectAllAdvertises().table;
// int I = 0;
// foreach (DataRow item in table.Rows)
// {
// // bitmap = BitmapFactory.DecodeByteArray(AbdoService.GetByteImage(item[1].ToString()).ByteImage, 0, AbdoService.GetByteImage(item[1].ToString()).ByteImage.Length);
// GroupLst.Add(new Advertise_TableItem(item[1].ToString(), item[2].ToString(), item[3].ToString(), item[0].ToString()));
// List<Replies_TableItem> child = new List<Replies_TableItem>();
// //child.Add(new Replies_TableItem("mohammed", "comment 1 "));
// //child.Add(new Replies_TableItem("ahmed", "comment 2 "));
// var childTable = AbdoService.selectReply(item[0].ToString());
// foreach (DataRow childItem in childTable.table.Rows)
// {
// child.Add(new Replies_TableItem(childItem[1].ToString(), childItem[2].ToString()));
// }
// ChildLst.Add(GroupLst[I], child);
// I++;
// }
//}
//else
//{
// if_connected();
//}
////adb = new Advertise_HomeScreenAdapter(this, tableItems);
////listview.Adapter = adb;
//Adapter = new ExpandableListViewAdapter(this, GroupLst, ChildLst);
UpdatePB uptask = new UpdatePB(this, listview, this);
uptask.Execute("paramter");
//uptask.GetResult();
//listview.SetAdapter(Adapter);
//for (int i = 0; i < Adapter.GroupCount; i++)
//{
// listview.ExpandGroup(i);
//}
listview.GroupClick += Listview_GroupClick;
startServices();
//////RunOnUiThread(() => progressBar.Visibility = ViewStates.Invisible);
}
you can not touch the UI from a background thread so you need to move this line:
listview.SetAdapter(Adapter);
for (int i = 0; i < Adapter.GroupCount; i++)
{
listview.ExpandGroup(i);
}
from RunInBackground(string[] #params) to OnPostExecute(string result)
also move if_connected(); from RunInBackground(string[] #params) to OnPostExecute(string result) because if_connected(); shows a dialog it can not be run in background
And add your complete Stack Trace of error to your question because there may be some other errors there in addition to mentioned ones.

C# : set default payment method in stripe

I am new in stripe, how can we set default payment method in stripe.
And can we pass cardId/sourceId to charge customer along with customerId.
Code:-
private static async Task<string> ChargeCustomer(string customerId)
{
return await System.Threading.Tasks.Task.Run(() =>
{
var myCharge = new StripeChargeCreateOptions
{
Amount = 50,
Currency = "gbp",
Description = "Charge for property sign and postage",
CustomerId = customerId
};
var chargeService = new StripeChargeService();
var stripeCharge = chargeService.Create(myCharge);
return stripeCharge.Id;
});
}
And 1 more question, how to get charge-list, I am using below code but getting exception(conversion error):-
private IEnumerable<StripeCharge> GetChargeList()
{
var chargeService = new StripeChargeService();
return chargeService.List();
}
This is what I ended up doing. Not sure why Stripe Checkout didn't set the card for the subscription setup as the default. Anyway, this fires triggered from the payment_intent.succeeded web hook. Sure there is a better way, but...
var customerService = new CustomerService(Configs.STRIPE_SECRET_KEY);
var c = customerService.Get(pi.CustomerId);
if (!string.IsNullOrEmpty(c.InvoiceSettings.DefaultPaymentMethodId)) {
status = "already has default payment method, no action";
hsc = HttpStatusCode.OK;
return;
}
var paymentMethodService = new PaymentMethodService(Configs.STRIPE_SECRET_KEY);
var lopm = paymentMethodService.ListAutoPaging(options: new PaymentMethodListOptions {
CustomerId = pi.CustomerId,
Type = "card"
});
if (!lopm.Any()) {
status = "customer has no payment methods";
hsc = HttpStatusCode.BadRequest;
return;
}
var pm = lopm.FirstOrDefault();
customerService.Update(pi.CustomerId, options: new CustomerUpdateOptions {
InvoiceSettings = new CustomerInvoiceSettingsOptions {
DefaultPaymentMethodId = pm.Id
}
});
hsc = HttpStatusCode.OK;
return;
We can pass cardId/BankAccountId/TokenId/SourceId in SourceTokenOrExistingSourceId property of StripeChargeCreateOptions,
private static async Task<string> ChargeCustomer(string customerId, string cardId)
{
try
{
return await System.Threading.Tasks.Task.Run(() =>
{
var myCharge = new StripeChargeCreateOptions
{
Amount = 50,
Currency = "gbp",
Description = "Charge for property sign and postage",
CustomerId = customerId,
SourceTokenOrExistingSourceId = cardId
};
var chargeService = new StripeChargeService();
var stripeCharge = chargeService.Create(myCharge);
return stripeCharge.Id;
});
}
catch(Exception ex)
{
return "";
}
}
To set/change default payment method:-
public void ChangeDefaultPayment(string customerId, string sourceId)
{
var myCustomer = new StripeCustomerUpdateOptions();
myCustomer.DefaultSource = sourceId;
var customerService = new StripeCustomerService();
StripeCustomer stripeCustomer = customerService.Update(customerId, myCustomer);
}
Still looking for how to get charge-list.

Navigate to another ContentPage from async method before the async method returns a value

I need to start Another ContentPage before this async method returns value:
public class GettingCountry : ContentPage
{
public static List<string> CountriesList = new List<string>();
MainPage mainPage = new MainPage();
public async Task<List<RootObject>> FetchAsync(string url)
{
string jsonString;
using (var httpClient = new System.Net.Http.HttpClient())
{
var stream = await httpClient.GetStreamAsync(url);
StreamReader reader = new StreamReader(stream);
jsonString = reader.ReadToEnd();
}
var listOfCountries = new List<RootObject>();
var responseCountries = JArray.Parse(JObject.Parse(jsonString)["response"]["items"].ToString());
foreach (var countryInResponse in responseCountries)
{
var rootObject = new RootObject((int)countryInResponse["id"], (string)countryInResponse["title"]);
CountriesList.Add(rootObject.Title);
}
//I NEED TO NAVIGATE TO FillingPage() FROM HERE:
await Navigation.PushAsync(new FillingPage());
//await Navigation.PushModalAsync(new NavigationPage(new FillingPage()));
return listOfCountries;
}
The page that is need to be started is:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FillingPage : ContentPage
{
public FillingPage ()
{
GettingCountry gettingCountry = new GettingCountry();
Label header = new Label
{
Text = "Заполните бланк",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
TextColor = Color.Blue
};
Entry nameEntry = new Entry()
{
Placeholder = "Имя",
};
Entry surnameEntry = new Entry()
{
Placeholder = "Фамилия"
};
Picker countryPicker = new Picker()
{
Title = "Страна",
VerticalOptions = LayoutOptions.CenterAndExpand
};
foreach (string country in GettingCountry.CountriesList)
{
countryPicker.Items.Add(country);
}
SearchBar townSearchBar = new SearchBar()
{
Placeholder = "Город",
SearchCommand = new Command(() =>
{
})
};
SearchBar universitySearchBar = new SearchBar()
{
Placeholder = "Университет",
SearchCommand = new Command(() =>
{
})
};
Button myButton = new Button()
{
TextColor = Color.Green,
Text = "Выполнить",
FontSize = 22
};
// Accomodate iPhone status bar.
this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
// Build the page.
this.Content = new StackLayout
{
Children =
{
header,
nameEntry,
surnameEntry,
countryPicker,
townSearchBar,
universitySearchBar,
myButton
}
};
}
}
}
But this code await Navigation.PushAsync(new FillingPage()); works well only when I press a button. When I press a button the needed page starts well. But the same code inside a method does not work. I have debagged it. It goes to a FillingPage() but doesn`t launches it when I try to launch it from inside the async method.
This is likely a result of the operation not being performed on the main thread. Try wrapping your code like this:
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PushAsync(new FillingPage());
}
Edit: After a private message, I learned that there was not enough information in the question to know the real issue. The application is calling FetchAsync in Application.OnStart and it's not part of the view hiearchy at all so navigation methods would not work. The following was provided:
protected override void OnStart ()
{
getCountry();
}
private async void getCountry()
{
var url = "...";
GettingCountry gettingCountry = new GettingCountry();
await gettingCountry.FetchAsync(url);
}
GettingCountry is a ContentPage being used like some kind of data access class and it's not currently part of the UI as MainPage is set to something else. A quick hack would be something more like:
private async void getCountry()
{
var url = "...";
GettingCountry gettingCountry = new GettingCountry();
var data = await gettingCountry.FetchAsync(url);
await MainPage.Navigation.PushAsync(new FillingPage(data));
}
I would suggest two further areas to look at improving.
Consider refactoring GettingCountry as it does not need to be a ContentPage.
Investigate an alternative calling so that async void is not used.

route line does not appear using MkMap in xamarin ios

i want to draw route line using mkMap in xamarin ios. my code is working correctly but it does not show route line between points. my code is given below
my first picture shows the starting annotation point and second picture shows ending annotation point
MapView Code:
private MKMapView _map;
private MapDelegate _mapDelegate;
public QiblaCompassVC (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
_map = new MKMapView(mapsView.Bounds)
{
MapType = MKMapType.Standard,
ShowsUserLocation = true,
ZoomEnabled = true,
ScrollEnabled = true
};
//_map = new MKMapView(mapsView.Bounds);
// _map.ShowsUserLocation = true;
_mapDelegate = new MapDelegate();
_map.Delegate = _mapDelegate;
//mapsView.Add(_map);
View = _map;
var target = new CLLocationCoordinate2D(30.3753, 69.3451);
var viewPoint = new CLLocationCoordinate2D(21.3891, 39.8579);
var annotation = new mapAnnotation(new CLLocationCoordinate2D(30.3753, 69.3451), "Pakistan", "Countery of love");
_map.AddAnnotation(annotation);
var annotation1 = new mapAnnotation(new CLLocationCoordinate2D(21.3891, 39.8579), "Makka", "Allah home");
_map.AddAnnotation(annotation1);
var camera = MKMapCamera.CameraLookingAtCenterCoordinate(target, viewPoint, 500);
_map.Camera = camera;
createRoute();
//CLLocationCoordinate2D coords = new CLLocationCoordinate2D(30.3753, 69.3451);
//MKCoordinateSpan span = new MKCoordinateSpan(MilesToLatitudeDegrees(20), MilesToLongitudeDegrees(20, coords.Latitude));
//_map.Region = new MKCoordinateRegion(coords, span);
}
public void createRoute()
{
var dict = new NSDictionary();
var orignPlaceMark = new MKPlacemark(new CLLocationCoordinate2D(30.3753, 69.3451), dict);
var sourceItem = new MKMapItem(orignPlaceMark);
//End at Xamarin Cambridge Office
var destPlaceMark = new MKPlacemark(new CLLocationCoordinate2D(21.3891, 39.8579), dict);
var destItem = new MKMapItem(destPlaceMark);
var request = new MKDirectionsRequest
{
Source = sourceItem,
Destination = destItem,
RequestsAlternateRoutes = true,
};
var directions = new MKDirections(request);
directions.CalculateDirections((response, error) =>
{
if (error != null)
{
Console.WriteLine(error.LocalizedDescription);
}
else
{
//Add each Polyline from route to map as overlay
foreach (var route in response.Routes)
{
_map.AddOverlay(route.Polyline);
}
}
});
}
MapDelegate Code:
class MapDelegate : MKMapViewDelegate
{
public override MKOverlayRenderer OverlayRenderer(MKMapView mapView, IMKOverlay overlay)
{
if (overlay is MKPolyline)
{
var route = (MKPolyline)overlay;
var renderer = new MKPolylineRenderer(route) { StrokeColor = UIColor.Blue };
return renderer;
}
return null;
}
public override MKOverlayView GetViewForOverlay(MKMapView mapView, IMKOverlay overlay)
{
if (overlay is MKPolyline)
{
// return a view for the polygon
MKPolyline l_polyline = overlay as MKPolyline;
MKPolylineView l_polylineView = new MKPolylineView(l_polyline);
MKPolylineRenderer l_polylineRenderer = new MKPolylineRenderer(l_polyline);
l_polylineView.FillColor = UIColor.Blue;
l_polylineView.StrokeColor = UIColor.Red;
return l_polylineView;
}
return null;
}
}
The problem is in the GetViewForOverlay method,
The overlay parameter is not not of type MKPolyline, it is a wrapper containing the MKPolyline, this is how to get it :
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
...
}
Source from xamarin forum

Categories

Resources