I am trying to show toast message in android and iOS from xamarin.forms project using Dependency Service. In iOS project message is shown on MainPage or NavigationPage. but when I navigate a second page on button click using PushModalAsync, message is not displayed.
How I navigate the page
public LoginPage()
{
Device.BeginInvokeOnMainThread(() =>
{
CustomToast.LongMessage("Hiiiiii"); // Message shown
});
Navigation.PushModalAsync(new RegisterPage()); //Doesn't show
//var reg = new RegisterPage();
//Application.Current.MainPage = reg; // toast shown here
}
Code for alertController in iOS :
const double SHORT_DELAY = 2.0;
NSTimer alertDelay;
UIAlertController alert;
public void LongAlert(string message)
{
ShowAlert(message, LONG_DELAY);
}
public void ShortAlert(string message)
{
ShowAlert(message, SHORT_DELAY);
}
void ShowAlert(string message, double seconds)
{
try
{
if (alert == null && alertDelay == null)
{
alertDelay = NSTimer.CreateScheduledTimer(seconds, (obj) =>
{
Device.BeginInvokeOnMainThread(() =>
{
DismissMessage();
});
});
Device.BeginInvokeOnMainThread(() =>
{
try
{
alert = UIAlertController.Create("", message, UIAlertControllerStyle.ActionSheet);
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
}
catch (Exception ex)
{
var Error = ex.Message;
}
});
}
}
catch (Exception ex)
{
TraceLog("Message iOS ShowAlert : " + ex.Message);
}
}
void DismissMessage()
{
if (alert != null)
{
alert.DismissViewController(true, null);
alert = null;
}
if (alertDelay != null)
{
alertDelay.Dispose();
alertDelay = null;
}
}
And I call this from my register page constructor
Device.BeginInvokeOnMainThread(() =>
{
CustomToast.LongMessage("Hiiiiii");
});
It doesn't go in catch anywhere but its not displayed also. can anyone please suggest some advice ?
This is because RegisterPage is a presented page on your LoginPage, UIApplication.SharedApplication.KeyWindow.RootViewController this code can't retrieve a correct view controller for RegisterPage. It just presented an action sheet on the previous page, but your app has reached a new page then this Toast can be shown on the screen.
Firstly, you have to find out the top page on the window:
UIViewController topViewControllerWithRootViewController(UIViewController rootViewController)
{
if (rootViewController is UITabBarController)
{
UITabBarController tabBarController = (UITabBarController)rootViewController;
return topViewControllerWithRootViewController(tabBarController.SelectedViewController);
}
else if (rootViewController is UINavigationController)
{
UINavigationController navigationController = (UINavigationController)rootViewController;
return topViewControllerWithRootViewController(navigationController.VisibleViewController);
}
else if (rootViewController.PresentedViewController != null)
{
UIViewController presentedViewController = rootViewController.PresentedViewController;
return topViewControllerWithRootViewController(presentedViewController);
}
return rootViewController;
}
Secondly, adjust your presenting code like:
Device.BeginInvokeOnMainThread(() =>
{
try
{
alert = UIAlertController.Create("", messages, UIAlertControllerStyle.ActionSheet);
topViewControllerWithRootViewController(UIApplication.SharedApplication.KeyWindow.RootViewController).PresentViewController(alert, true, null);
}
catch (Exception ex)
{
var Error = ex.Message;
}
});
At last, you could show your toast using Navigation.PushModalAsync(new RegisterPage());
Related
enter image description hereHi Thanks in advance i have facing a problem in my xamarin forms ios. Problem is that when i want to purchase product it thrown an exception that cannot to connect to itune store my same code in working fine on xamarin forms android.My code for restore purchases is working fine.
Here is my code for make purchases
private async Task<bool> MakePurchase(string ProductId, string Payload)
{
if (!CrossInAppBilling.IsSupported)
{
return false;
}
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync();
if (!connected)//Couldn't connect to billing, could be offline,
alert user
{
DependencyService.Get<IToast>().Show("Something went
wrong or you may not connected with the internet!");
return false;
}
//try to purchase item
var purchase = await billing.PurchaseAsync(ProductId,
ItemType.InAppPurchase, Payload);
if (purchase == null)
{
return false;
}
else
{
//Purchased, save this information
var responseId = purchase.Id;
var responseToken = purchase.PurchaseToken;
var state = purchase.State;
return true;
}
}
catch (InAppBillingPurchaseException ex)
{
if (ex.PurchaseError == PurchaseError.DeveloperError)
{
DependencyService.Get<IToast>().Show("DeveloperError");
ex.Message.ToString();
}
else if (ex.PurchaseError == PurchaseError.AlreadyOwned)
{
DependencyService.Get<IToast>().Show("AlreadyOwned");
return true;
}
else if(ex.PurchaseError == PurchaseError.BillingUnavailable)
{
DependencyService.Get<IToast>
().Show("BillingUnavailable");
return false;
}
else if(ex.PurchaseError == PurchaseError.InvalidProduct)
{
DependencyService.Get<IToast>().Show("InvalidProduct");
return false;
}
else if(ex.PurchaseError == PurchaseError.ItemUnavailable)
{
DependencyService.Get<IToast>().Show("ItemUnavailable");
return false;
}
else if(ex.PurchaseError == PurchaseError.GeneralError)
{
DependencyService.Get<IToast>().Show("General Error");
return false;
}
//Something bad has occurred, alert user
}
finally
{
//Disconnect, it is okay if we never connected
await billing.DisconnectAsync();
}
return false;
}
I have xamarin.forms app contains a listview which will load values from Rest API.Which is working fine.I have button just above the listview.When I click on the button, the listview API call will be placed again and the listview should update. But stuck at this update part.I am not using MVVM pattern.The listview listing portion is an async Task.I am calling the async task again when the button click, but App gets crash. Is it due to calling the async task again from button click? Any help is appreciated.
Here is My code.
namespace app
{
public partial class List : ContentPage
{
PendingWeekRange pendingWeekRange = new PendingWeekRange();
public TimeSheetList()
{
InitializeComponent();
Task.Run(async () =>
{
await LoadScreenItems();
});
}
async Task LoadScreenItems()
{
await Task.Run(async () => {
try
{
// Doing some stuff
await loadTimeSheetList();
}
catch (Exception)
{
}
});
}
async Task loadTimeSheetList()
{
await Task.Run(() => { + string postdataForPendingList = "{\"date\":\"" + "1" + "\"}";
APICall callForAPICallResult = new APICall("/API/ListMobile/ListForApproval", postdataForList, loadingIndicator);
try
{
List<ListData> resultObjForPendingTimeSheetList = callForAPICallResult<List<ListData>>();
if (resultObjForPendingTimeSheetList != null)
{
TimesheetList.ItemsSource = resultObjForPendingTimeSheetList;
screenStackLayout.VerticalOptions = LayoutOptions.FillAndExpand;
TimesheetList.IsVisible = true;
}
else
{
}
}
catch (Exception)
{
}
});
}
async void Button_Tapped(object sender, EventArgs e)
{
try
{
// Calling my listview again. After calling app gets crash
Task.Run(async () => await loadTimeSheetList());
}
catch (Exception ex) { }
}
}
}
A few things before getting to the problem. You've got async/await all wrong, go though Async Programming
Task.Run runs the passed action on a different thread, if you make changes to UI elements on this thread, your app will definitely(take my word) crash.
If you want to make async call at page launch, make use of OnAppearing method (if you only want to call once, maintain a flag)
Do not change the ItemsSource of a list view frequently, just clear and add items to it.
namespace app
{
public partial class List : ContentPage
{
PendingWeekRange pendingWeekRange = new PendingWeekRange();
private ObservableCollection<ListData> TimesheetObservableCollection = new ObservableCollection<ListData>();
public TimeSheetList()
{
InitializeComponent();
TimesheetList.ItemsSource = TimesheetObservableCollection;
}
protected override async OnAppearing()
{
// flag for first launch?
await LoadScreenItems();
}
async Task LoadScreenItems()
{
try
{
// Doing some stuff
TimesheetObservableCollection.Clear();
TimesheetObservableCollection.AddRange(await GetTimeSheetList());
}
catch (Exception)
{
//handle exception
}
}
async Task<List<ListData>> GetTimeSheetList()
{
string postdataForPendingList = "{\"date\":\"" + "1" + "\"}";
APICall callForAPICallResult = new APICall("/API/ListMobile/ListForApproval", postdataForList, loadingIndicator);
try
{
return callForAPICallResult<List<ListData>>();
}
catch (Exception)
{
// handle exception
}
}
async void Button_Tapped(object sender, EventArgs e)
{
try
{
// Calling my listview again. After calling app gets crash
TimesheetObservableCollection.Clear();
TimesheetObservableCollection.AddRange(await GetTimeSheetList());
}
catch (Exception ex) { }
}
}
}
#Androdevil,
Update your loadTimeSheetList with this,
async Task loadTimeSheetList()
{
try
{
// I am calling my API for Listview here.
List<TimeSheetListData> resultObjForPendingTimeSheetList = await callForPendingTimeSheetList.APICallResult<List<TimeSheetListData>>();
if (resultObjForPendingTimeSheetList != null)
{
TimesheetList.ItemsSource = resultObjForPendingTimeSheetList;
screenStackLayout.VerticalOptions = LayoutOptions.FillAndExpand;
TimesheetList.IsVisible = true;
}
else
{
}
}
catch (Exception)
{
}
}
Complete Scenario
I have an add icon on one page, it will show camera and gallery options when tap. If choose the camera, I will open another content page and open camera there. But the captured picture is not showing in the UI. Same for the gallery, selected image from the gallery is not showing in UI. This feature is working fine in android and not working in IOS.
Codes
When click add icon
string action = await DisplayActionSheet(null, "Cancel", null, "Camera", "Gallery");
if (action == "Camera")
{
await Navigation.PushModalAsync(new NewTweetPage("Camera"));
}
else if (action == "Gallery")
{
await Navigation.PushModalAsync(new NewTweetPage("Gallery"));
}
When entering next page
public NewTweetPage(String medium)
{
InitializeComponent();
if (medium == "Camera" )
{
OpenMyCamera();
}
else if(medium == "Gallery")
{
OpenMygallery();
}
}
public async void OpenMyCamera()
{
try
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("Camera", "No camera available.", "OK");
return;
}
_mediaFile = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Sample",
Name = "test.jpg",
AllowCropping = true
});
if (_mediaFile == null)
return;
tweetPicture.Source = ImageSource.FromStream(() =>
{
isPicture = true;
return _mediaFile.GetStream();
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception:>" + ex);
}
}
public async void OpenMygallery()
{
try
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await DisplayAlert("Gallery", ":( No photos available.", "OK");
return;
}
_mediaFile = await CrossMedia.Current.PickPhotoAsync();
if (_mediaFile == null)
return;
tweetPicture.Source = ImageSource.FromStream(() =>
{
isPicture = true;
return _mediaFile.GetStream();
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception:>" + ex);
}
}
The same code is working fine in profile page part, but in that case, there is no page navigation, everything is happening on the same page.
Don't know what is the problem with the current code, please help me to solve this issue.
Putting navigation commands in the constructor can cause issues. I would recommend putting them in the OnAppearing override. Also, instead of having a try...catch around a large section of code, you should handle null-checks or similar in code.
I have a requirement for my xamarin cross platform application that as soon as app start up .QR Scanner set in to read the code. on completing scanning a beep will be ring up.and app again ready for next scanning how can i get this done. what i have done is on button click scanner start, its read code, then i have to press button again to start it again.
public HomePage()
{
Button scanBtn = new Button
{
Text = "Scan Barcode",
HorizontalOptions = LayoutOptions.FillAndExpand,
};
scanBtn.Clicked += async (sender, args) =>
{
var scanResult = await Acr.BarCodes.BarCodes.Instance.Read();
if (!scanResult.Success)
{
await this.DisplayAlert("Alert ! ", "Sorry ! \n Failed to read the Barcode !", "OK");
}
else
{
var endpoint = new EndpointAddress("http://192.168.15.33/SMS/WebServices/SMSService.svc");
var binding = new BasicHttpBinding
{
Name = "basicHttpBinding",
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647
};
TimeSpan timeout = new TimeSpan(0, 0, 30);
binding.SendTimeout = timeout;
binding.OpenTimeout = timeout;
binding.ReceiveTimeout = timeout;
_client = new SMSServiceClient(binding, endpoint);
_client.ValidateStudentAsync("123-admin");
_client.ValidateStudentCompleted += _client_ValidateStudentCompleted; ;
// await this.DisplayAlert("Scan Successful !", String.Format("Barcode Format : {0} \n Barcode Value : {1}", scanResult.Format, scanResult.Code), "OK");
}
};
Content = new StackLayout
{
Children = {
scanBtn
}
};
}
and in app.cs
public class App : Application
{
public App()
{
// The root page of your application
MainPage = new HomePage();
}
protected override void OnStart()
{
MainPage = new HomePage();
}
protected override void OnSleep()
{
MainPage = new HomePage();
}
protected override void OnResume()
{
MainPage = new HomePage();
}
}
You can use ZXing.Net.Mobile for Forms to read QR codes. To initialize this plugin you should call method to init into each project (Android, iOS, UWP) like this:
For Android in MainActivity.cs class call:
ZXing.Net.Mobile.Forms.Droid.Platform.Init();
For iOS in AppDeletage.cs class call
ZXing.Net.Mobile.Forms.iOS.Platform.Init();
And finally to read QR Codes:
private async void Scan() {
var scanPage = new ZXingScannerPage();
scanPage.OnScanResult += (result) => {
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread( async () => {
await Navigation.PopAsync();
await DisplayAlert("Scanned Barcode", result.Text, "OK");
});
};
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
}
I am trying to delete set of records under my ASP.NET Application - API Controller. Here is my code from API Controller:
public JsonResult Delete([FromBody]ICollection<ShoppingItemViewModel> vm)
{
if (ModelState.IsValid)
{
try
{
var items = Mapper.Map<IEnumerable<ShoppingItem>>(vm);
_repository.DeleteValues(items, User.Identity.Name);
return Json(null);
}
catch (Exception Ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(null);
}
}
else
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(null);
}
}
And here is my AngularJS Controller part taking care of this:
$scope.RemoveItems = function () {
$scope.isBusy = true;
$http.delete("/api/items", $scope.items)
.then(function (response) {
if (response.statusText == "OK" && response.status == 200) {
//passed
for (var i = $scope.items.length - 1; i > -1; i--) {
if ($scope.items[i].toRemove == true) {
$scope.items.splice(i, 1);
}
}
}
}, function (err) {
$scope.errorMessage = "Error occured: " + err;
}).finally(function () {
$scope.isBusy = false;
});
}
For unknown reason, this works like a charm in my POST method but not in the Delete Method. I believe that the problem might be caused by the fact, that DELETE method only accepts Integer ID?
If that is the case, what is the correct way how to delete multiple items with one call?