I am using a LocalNotification plugin(nuget) to generate a Phone
Notification upon an event, which works fine. If the user navigates to
a page which lists the notifications and selects one, it goes to the
NotificationPageModel.cs page. From there they press a button which
navigates them away, which works as long as it is manually navigated
to.
If the user clicks on the phone's notification
(app.xaml.cs:OnLocalNotificationTapped) then when trying to navigate
away, it generates a FreshTinyIoC.TinyIoCResolutionException.
How can I navigate away from the page when the user has clicked the
notification (masterDetailNav.Navigation.PushModalAsync(npage);) ?
Relevant versions: Xamarin.Forms v3.6.0.264807 FreshMvvm v3.0.0
Plugin.LocalNotification by Thudugala v4.0.5
App.xaml.cs
void OnLocalNotificationTapped(NotificationTappedEventArgs e)
{
var _logger = FreshMvvm.FreshIOC.Container.Resolve<ILoggingService>();
_logger.Info("Pressed notification: {0}", e.Data);
Notification notification = new Notification();
if (string.IsNullOrWhiteSpace(e.Data))
{
return;
}
else
{
notification = JsonConvert.DeserializeObject<Notification>(e.Data);
}
//CoreMethods.PushPageModel<NotificationPageModel>(notification); //cant find coremethods
var npage = FreshPageModelResolver.ResolvePageModel<NotificationPageModel>(notification);
masterDetailNav.Navigation.PushModalAsync(npage);
}
NotificationPageModel.cs:
public async void Cancel()
{
_logger.Info("Notification {0} Cancel: {1}", Notification.Gateway, Notification.TimeStamp);
//Navigate away from page
//await CoreMethods.PopPageModel(); //this only works if manually navigated to
//attempt #1
await CoreMethods.PushPageModel<HomePageModel>();
}
public async void Remove()
{
_logger.Info("Notification {0} Remove: {1}", Notification.Gateway, Notification.TimeStamp);
//Remove notification
try
{
_user.Notifications.Remove(Notification);
}
catch (Exception ex)
{
_logger.Warn("Remove Notification: {0}", ex.Message);
}
//Navigate away from page
//await CoreMethods.PopPageModel(); //this only works if manually navigated to
//attempt #2
var hpage = FreshPageModelResolver.ResolvePageModel<HomePageModel>();
await CoreMethods.PushPageModelWithNewNavigation<HomePageModel>(hpage);
}
Stacktrace
FreshTinyIoC.TinyIoCResolutionException Message=Resolve failed:
IFreshNavigationService Source=mscorlib StackTrace: at
FreshTinyIoC.FreshTinyIoCContainer.ResolveInternal
(FreshTinyIoC.FreshTinyIoCContainer+TypeRegistration registration,
FreshTinyIoC.NamedParameterOverloads parameters,
FreshTinyIoC.ResolveOptions options) [0x000f7] in
C:\Projects\FreshMvvm\src\FreshIOC\FreshTinyIOC.cs:3142 at
FreshTinyIoC.FreshTinyIoCContainer.Resolve (System.Type resolveType,
System.String name) [0x00000] in
C:\Projects\FreshMvvm\src\FreshIOC\FreshTinyIOC.cs:1211 at
FreshTinyIoC.FreshTinyIoCContainer.Resolve[ResolveType] (System.String
name) [0x00000] in
C:\Projects\FreshMvvm\src\FreshIOC\FreshTinyIOC.cs:1332 at
FreshMvvm.FreshTinyIOCBuiltIn.Resolve[ResolveType] (System.String
name) [0x00000] in
C:\Projects\FreshMvvm\src\FreshMvvm\FreshTinyIOCBuiltIn.cs:31 at
FreshMvvm.PageModelCoreMethods.PushNewNavigationServiceModal
(FreshMvvm.IFreshNavigationService newNavigationService,
FreshMvvm.FreshBasePageModel[] basePageModels, System.Boolean animate)
[0x00073] in
C:\Projects\FreshMvvm\src\FreshMvvm\PageModelCoreMethods.cs:178 at
FreshMvvm.PageModelCoreMethods.PushPageModelWithNewNavigation[T]
(System.Object data, System.Boolean animate) [0x00060] in
C:\Projects\FreshMvvm\src\FreshMvvm\PageModelCoreMethods.cs:235 at
cdaxrobot.PageModels.NotificationPageModel.Go () [0x00225] in
C:\Work\RobotApp\App\cdaxrobot\cdaxrobot\cdaxrobot\PageModels\NotificationPageModel.cs:113
at
System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b__7_0
(System.Object state) [0x00000] in
/Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1021
at Android.App.SyncContext+<>c__DisplayClass2_0.b__0 ()
[0x00000] in <11f101b564894ca7af6c482ddc51c698>:0 at
Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in
<11f101b564894ca7af6c482ddc51c698>:0 at
Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr
native__this) [0x00009] in <11f101b564894ca7af6c482ddc51c698>:0 at
(wrapper dynamic-method)
Android.Runtime.DynamicMethodNameCounter.41(intptr,intptr)
Thank you for the response.
I managed to fix it by creating a new NavigationContainer:
notificationPage = FreshMvvm.FreshPageModelResolver.ResolvePageModel<NotificationPageModel>(true);
notificationContainer = new FreshNavigationContainer(notificationPage, Models.Constants.NotificationContainer);
void OnLocalNotificationTapped(NotificationTappedEventArgs e)
{
MainPage = notificationContainer;
}
and then in the page I do this:
if (CameFromNotification == true)
{
_logger.Debug("Navigating back to MainContainer");
CoreMethods.SwitchOutRootNavigation(Models.Constants.MainContainer);
}
else
{
_logger.Debug("Navigating by popping the stack");
await CoreMethods.PopPageModel();
}
Related
I have a Xamarin iOS application I am building. For the Authentication and Authorization I am using Identity Server. To logon I am using IdentityModel.OidcClient using version 4.0.0.
When a user click on a Login button I call the Identity Server using:
_result = await _client.LoginAsync(new LoginRequest());
I am redirected to my identity server and can succesfully login. After login the browser disappears but the code never reaches the next line:
_result = await _client.LoginAsync(new LoginRequest());
if (_result.IsError)
{
return;
}
I have found many example and they all tell me to do the following. I have also tried with the demo Identity Server and same issue.
This is the IdentityModel.OidcClient.Browser.IBrowser implementation:
public class ASWebAuthenticationSessionBrowser : IBrowser
{
ASWebAuthenticationSession _asWebAuthenticationSession;
public ASWebAuthenticationSessionBrowser()
{
Debug.WriteLine("ctor");
}
public Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<BrowserResult>();
try
{
_asWebAuthenticationSession = new ASWebAuthenticationSession(
new NSUrl(options.StartUrl),
new NSUrl(options.EndUrl).Scheme,
(callbackUrl, error) =>
{
tcs.SetResult(CreateBrowserResult(callbackUrl, error));
_asWebAuthenticationSession.Dispose();
});
// iOS 13 requires the PresentationContextProvider set
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
_asWebAuthenticationSession.PresentationContextProvider = new PresentationContextProviderToSharedKeyWindow();
_asWebAuthenticationSession.Start();
}
catch (Exception ex)
{
throw;
}
return tcs.Task;
}
class PresentationContextProviderToSharedKeyWindow : NSObject, IASWebAuthenticationPresentationContextProviding
{
public UIWindow GetPresentationAnchor(ASWebAuthenticationSession session)
{
return UIApplication.SharedApplication.KeyWindow;
}
}
private static BrowserResult CreateBrowserResult(NSUrl callbackUrl, NSError error)
{
if (error == null)
return new BrowserResult
{
ResultType = BrowserResultType.Success,
Response = callbackUrl.AbsoluteString
};
if (error.Code == (long)ASWebAuthenticationSessionErrorCode.CanceledLogin)
return new BrowserResult
{
ResultType = BrowserResultType.UserCancel,
Error = error.ToString()
};
return new BrowserResult
{
ResultType = BrowserResultType.UnknownError,
Error = error.ToString()
};
}
}
I am using Xamarin.Forms and netstandard 2.0.
My redirect uri: RedirectUri = "wodtracker.app://callback"
I am using the zing mobile scanner and it works well however I am calling the same action from a different button and it's refusing to scan it brings up the preview window ok but won't invoke the scan.
public async void BtnScanStockTakeItem_Clicked(object sender, EventArgs e)
{
var scanPage = new ZXingScannerPage();
scanPage.ToggleTorch();
scanPage.IsScanning = true;
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
}
}
I am then calling the above from another button method lets say the saved function
private async void SaveFunction(object sender, EventArgs e)
{
foreach (var item in transferList)
{
int z = await restServices.PostStockTakeTransaction(item);
}
Preferences.Set("StockTakeWarehouse", pickStockTake.SelectedIndex);
WarehouseName = pickStockTake.SelectedItem.ToString();
bool x = await DisplayAlert("Test", "Item Saved", "ReScan", "Cancel");
if (x)
{
BtnScanStockTakeItem_Clicked(sender, e);
//this is where it rescans the item
}
}
String thing is I am getting no logcat errors or nothing the viewport of the scanner shows but just won't accept the scan some reason same barcode format as before.
For anyone else who faces a simlar problem it was the fact my camera thread was not properly called I had to do
Device.BeginInvokeOnMainThread(async () =>
{
BtnScanStockTakeItem_Clicked(sender, e);
});
Works as expected
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)
{
}
}
Hi I am writing a custom login dialog. I want to display a progress indicator while my users credentials are verified. When I run my button handler the progress indicator does not appear even though I call the setVisible(true) method. Also the text is not visible either.
#FXML
private void handleLoginButton2(ActionEvent event) {
//username and password required
if (userNameTextField.getText().isEmpty() || passwordTextField.getText().isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Login Validation");
alert.setHeaderText("Validation Failed");
String msg = "Please input user name and password.";
alert.setContentText(msg);
alert.showAndWait();
} else {
try {
loginProgressIndicator.setVisible(true);
loginStatusText.setText("Authorising...");
Class.forName(JDBC_DRIVER2);
String user = userNameTextField.getText();
String password = passwordTextField.getText();
Connection conn = DriverManager.getConnection(JDBC_URL2, user, password);
loginStatusText.setText("Authorisation Successful");
loginProgressIndicator.setVisible(false);
//close login window
windowManager.closeLoginWindow();
//open main screen
windowManager.showMainView();
} catch (ClassNotFoundException | SQLException | IOException ex) {
Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
loginStatusText.setText("Authorisation Failed");
loginProgressIndicator.setVisible(false);
}
}
}
Do the DB access from a task running in a seperate thread and handle the results in the onSucceeded/onFailed event handlers:
public class AuthorizeTask extends Task<Void> {
private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/victorydb?zeroDateTimeBehavior=convertToNull";
private final String user;
private final String password;
public AuthorizeTask(String user, String password) {
if (user == null || password == null) {
throw new IllegalArgumentException();
}
this.user = user;
this.password = password;
}
#Override
protected Void call() throws Exception {
Class.forName(JDBC_DRIVER);
Connection conn = DriverManager.getConnection(JDBC_URL, this.user, this.password);
return null;
}
}
Task<Void> task = new AuthorizeTask(userNameTextField.getText(), passwordTextField.getText());
task.setOnFailed(evt -> {
Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, task.getException());
loginStatusText.setText("Authorisation Failed");
loginProgressIndicator.setVisible(false);
});
task.setOnSucceeded(evt -> {
// you may need to add a try/catch here
loginStatusText.setText("Authorisation Successful");
loginProgressIndicator.setVisible(false);
//close login window
windowManager.closeLoginWindow();
//open main screen
windowManager.showMainView();
});
loginProgressIndicator.setVisible(true);
loginStatusText.setText("Authorising...");
// run this on background thread to avoid blocking the application thread
new Thread(task).start();
You may want to change the type parameter of Task to Connection though, since you probably need to work with the Connection later...
I have a Xamarin.Forms application. I use ZXing.Mobile.Forms to scan a QR code; however, when I point the camera to a QR code the OnScanResult event is called twice. This is my code, a faithful reproduction of the one suggested here:
private async void onScanQRCode(object sender, EventArgs e)
{
var scanPage = new ZXingScannerPage(); // executed just once
scanPage.OnScanResult += (result) => {
// Stop scanning
scanPage.IsScanning = false; // executed twice (confused)
// Pop the page and show the result
Device.BeginInvokeOnMainThread(() => {
Navigation.PopAsync();
/* Business code */
Account scannedAccount = URLInterpreter.Accept(result.Text);
ViewModel.ProcessNewAccount(scannedAccount);
/* End of business code */
});
};
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
}
And this is the XAML for the button that calls the method above:
<Button x:Name="btnScanCode" Text="Scan QR Code" Clicked="onScanQRCode" />
Any pointers?
UPDATE
Apparently, this only happens when I'm debugging the app and the phone is connected to Visual Studio. If I launch the app without attaching the debugger, the event is fired once.
I had the same issue, this answer helped me to resolve it: Xamarin.Forms ZXing.Net.Mobile loosing current page after scan result on iOS 10
Your code should be:
private async void onScanQRCode(object sender, EventArgs e)
{
var scanPage = new ZXingScannerPage();
bool scanFinished = false;
scanPage.OnScanResult += (result) => {
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread(async () => {
if (!scanFinished)
{
scanFinished = true;
Account scannedAccount = URLInterpreter.Accept(result.Text);
ViewModel.ProcessNewAccount(scannedAccount);
await Navigation.PopAsync();
}
});
};
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
}
I tested on iOS 10.3.1 and ZXing.Net.Forms.Mobile 2.2.9.