My code crashes and gives the following error on simulator. It attempts to run the try block in the GetDataFromOdataService() method and throws an error and also issue an alert. I am using Xamarin.Form
using Simple.OData.Client;
using System.Threading.Tasks;
private ODataClient mODataClient;
protected async override void OnAppearing ()
{
base.OnAppearing ();
await InitializeDataService ();
await GetDataFromOdataService();
}
public async Task <bool> InitializeDataService(){
try {
mODataClient = new ODataClient ("http://services.odata.org/Northwind/Northwind.svc/");
}
catch {
await DisplayAlert("Error", "Connection Error", "OK", "Cancel");
System.Diagnostics.Debug.WriteLine("ERROR!");
}
return true;
}
public async Task<bool> GetDataFromOdataService (){
try {
myCustomers= await mODataClient.For("Customers").Top(10).FindEntriesAsync();
}
catch {
await DisplayAlert("Error", "Connection Error", "OK", "Cancel");
System.Diagnostics.Debug.WriteLine("ERROR!");
}
return true;
}
Couple issues:-
In the constructor it was doing var list = new ListView() which constrained it locally than setting the class level scope variable. This was therefore adjusted to list = new ListView().
The other thing, was in the GetTheData function where the items source was being assigned as list.ItemsSource = myList; where it needed changing to list.ItemsSource = Customers;.
I've repackaged the zip file up and sent to you. Let me know if this works for you? You should now be able to see all your customers in the ListView.
Related
I've been trying to call a Dialog Alert from my xamarin.forms app using IoC container so far I haven't been able to display an alert in my app using this code:
Code at my viewModel:
await _dialogService.DisplayMessageAsync("ERROR", "There are errors on your form!", "Cancel", null);
at my shared project I have this DialogService which is the one I call from my viewModel:
public class DialogService : IDialogService
{
readonly IDialogService _dialogService;
public DialogService()
{
_dialogService = DependencyService.Get<IDialogService>();
}
public void CloseAllDialogs()
{
_dialogService.CloseAllDialogs();
}
public async Task DisplayMessageAsync(string title, string message, string buttonCancelName, Action callback)
{
await _dialogService.DisplayMessageAsync(title, message, buttonCancelName, callback);
}
public async Task DisplayMessageConfirmAsync(string title, string message, string buttonOkName, string buttonCancelName, Action<bool> callback)
{
await _dialogService.DisplayMessageConfirmAsync(title, message, buttonOkName, buttonCancelName, callback);
}
}
so at my Xamarin.Android.XXXXX I have the implementation of my DialogService which is call from my DialogService at my Shared Project this is the code:
public class DialogService : IDialogService
{
List<AlertDialog> _openDialogs = new List<AlertDialog>();
public void CloseAllDialogs()
{
foreach (var dialog in _openDialogs)
{
dialog.Dismiss();
}
_openDialogs.Clear();
}
public async Task DisplayMessageAsync(string title, string message, string okButton, Action callback)
{
await Task.Run(() => Alert(title, message, okButton, callback));
}
public async Task DisplayMessageConfirmAsync(string title, string message, string okButton, string cancelButton, Action<bool> callback)
{
await Task.Run(() => AlertConfirm(title, message, okButton, cancelButton, callback));
}
bool Alert(string title, string content, string okButton, Action callback)
{
var activity = (Activity)Forms.Context;
//var activity = (Activity)Android.App.Application.Context;
var alert = new AlertDialog.Builder(Android.App.Application.Context);
//var alert = new AlertDialog.Builder(activity);
alert.SetTitle(title);
alert.SetMessage(content);
alert.SetNegativeButton(okButton, (sender, e) =>
{
if (!Equals(callback, null))
{
callback();
}
_openDialogs.Remove((AlertDialog)sender);
});
Device.BeginInvokeOnMainThread(() =>
{
AlertDialog dialog = alert.Show();
_openDialogs.Add(dialog);
dialog.SetCanceledOnTouchOutside(false);
dialog.SetCancelable(false);
});
return true;
}
bool AlertConfirm(string title, string content, string okButton, string cancelButton, Action<bool> callback)
{
var alert = new AlertDialog.Builder(Android.App.Application.Context);
alert.SetTitle(title);
alert.SetMessage(content);
alert.SetNegativeButton(cancelButton, (sender, e) =>
{
callback(false);
_openDialogs.Remove((AlertDialog)sender);
});
alert.SetPositiveButton(okButton, (sender, e) =>
{
callback(true);
_openDialogs.Remove((AlertDialog)sender);
});
Device.BeginInvokeOnMainThread(() =>
{
var dialog = alert.Show();
_openDialogs.Add(dialog);
dialog.SetCanceledOnTouchOutside(false);
dialog.SetCancelable(false);
});
return true;
}
}
so whenever the private method alert is called it throws an Exception like this:
Unhandled Exception:
Android.Views.WindowManagerBadTokenException: Unable to add window --
token null is not valid; is your activity running?
it can be corrected if I switch this line of code:
var alert = new AlertDialog.Builder(Android.App.Application.Context);
for this line of code:
var activity = (Activity)Forms.Context;
var alert = new AlertDialog.Builder(activity);
the problem of using this I get a Xamarin.Forms warning like this:
'Forms.Context' is obsolete: 'Context is obsolete as of version 2.5.
Please use a local context instead.'
and i'm a bit obsesive I dont like to have warning and try to maintain my code as updated as possible so, can somebody help me make this code work without needing to use obsolete code. because I found answers that just replacing (Activity)Forms.Context for Android.App.Application.Context would work, but in this case it isn't working at all.
hopefully someone can point me in the right direction because i haven't been able to find any documentation about this case specifically.
Problem is that Android.App.Application.Context is not always an Activity Context.
Xamarin removed this and added a new constructor for the Renderers which include the context. The problem comes in cases like this where you are working on something that is not a CustomRenderer.
For these cases, I use James' Plugin CurrentActivityPlugin which will keep track of the current activity for you. Find it here
Hope this helps.-
I am implementing an application in Xamarin with a login functionality. I am using Xamarin SecureStorage to store my username and password in Keychain. I am using Prism to segue between my pages.
I have an AuthenticationService that handles all saving and storing keychain data with SecureStorage. In my App.Xaml, I have a PubSubEvent below that handles when the user is authenticated or not.
var result = await NavigationService.NavigateAsync("/MainPage?title=Welcome");
if (!result.Success)
{
Console.WriteLine("Error {0}", result.Exception);
logger.Report(result.Exception, new Dictionary<string, string> { { "navigationPath", "/MainPage" } });
//MainPage = result.CreateErrorPage();
System.Diagnostics.Debugger.Break();
}
In my LoginPageViewModel I have the following binding based on my login button.
private async Task OnLoginUserCommandExecutedAsync()
{
try
{
MyPrivateSession response = new MyPrivateSession();
//Username/Password text is valid
response = await _authenticationService.LoginAsync(UserLogin);
if (!response.IsValidUser)
{
await PageDialogService.DisplayAlertAsync("", "Login Unsuccessful - Invalid User Id or Password", "Ok");
}
}
catch (AuthenticationException authEx)
{
await PageDialogService.DisplayAlertAsync("Error", authEx.Message, "Ok");
}
catch (Exception e)
{
Debugger.Break();
Logger.Log(e.Message, Category.Exception, Priority.Medium);
await PageDialogService.DisplayAlertAsync("Fatal Error", e.Message, "Ok");
}
}
Also in my LoginPageViewModel, I have the following code in onNavigatedTo to get saved login from SecureStorage
public override async void OnNavigatedTo(INavigationParameters parameters)
{
// Determine if there is a preferred account to use in cases where the user is logged out and sent directly to the login
// in this case the AssumedLogin isn't defined.
if (_authenticationService.AssumedLogin is null)
{
await _authenticationService.AssumeUserPriorToLogin();
if (!(_authenticationService.AssumedLogin is null))
{
UserLogin.Username = _authenticationService.AssumedLogin.Username;
UserLogin.Password = _authenticationService.AssumedLogin.Properties["password"];
//Segues to VIN Utilities
await OnLoginUserCommandExecutedAsync();
}
}
}
In my AuthenticationService, I have this at the end of my logic after accessing username and password from SecureStorage
var user = GetCurrentUser();
_container.UseInstance(user);
//Publish User Authenticated Event
_eventAggregator.GetEvent<UserAuthenticatedEvent>().Publish(user);
My problem is when a user manually logs in with the login button, everything works perfectly. However, when I'm adding logic on the LoginPageViewModel's OnNavigatedTo to automatically authenticate user when the user have a saved username and password. It kicks me out of MainPage and back to login page.
One my doubts is that, when I do it automatic login through navigation page, the application is not finished loading yet. The application would log OnNavigatedTo from MainPage first before "Loaded My App" in the AppDelegate. (see below) How can I ensure the iOS initializers finish first before I start segueing to the main page?
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Rg.Plugins.Popup.Popup.Init();
SlideOverKit.iOS.SlideOverKit.Init();
SfCheckBoxRenderer.Init();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Console.WriteLine("Loading My App");
LoadApplication(new App(new IOSInitializer()));
Console.WriteLine("Loaded My App");
return base.FinishedLaunching(app, options);
}
public class IOSInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
private void OnUserAuthenticated(IUser user)
{
var result = await NavigationService.NavigateAsync("/MainPage");
if (!result.Success)
{
Console.WriteLine("Error {0}", result.Exception);
//MainPage = result.CreateErrorPage();
System.Diagnostics.Debugger.Break();
}
}
I'm building a Xamarin app and for the geolocation, I'm using the GeolocatorPlugin
The problem is that once the code wants to get the position, the code exists without warning.
My class fields:
private Position position;
private IGeolocator locator = CrossGeolocator.Current;
My page constructor:
public MainPage()
{
InitializeComponent();
locator.PositionChanged += Locator_PositionChanged;
locator.PositionError += Locator_PositionError;
}
OnAppearing event is calling the getLocationPermission:
private async Task GetLocationPermission()
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.LocationWhenInUse);
if (status != PermissionStatus.Granted)
{
//Not granted, request permission
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.LocationWhenInUse))
{
// This is not the actual permission request
await DisplayAlert("Need your permission", "We need to access your location", "Ok");
}
// This is the actual permission request
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.LocationWhenInUse);
if (results.ContainsKey(Permission.LocationWhenInUse))
status = results[Permission.LocationWhenInUse];
}
//Already granted, go on
if (status == PermissionStatus.Granted)
{
//Granted, get the location
GetLocation();
await GetVenues();
await locator.StartListeningAsync(TimeSpan.FromMinutes(30), 500);
}
else
{
await DisplayAlert("Access to location denied", "We don't have access to your location.", "OK");
}
}
The permission is granted and gets to the GetLocation() method:
private async void GetLocation()
{
//var locator = CrossGeolocator.Current;
try
{
var myPosition = await locator.GetPositionAsync();
position = new Position(myPosition.Latitude, myPosition.Longitude);
}
catch (Exception ex)
{
throw;
}
if (position == null)
{
//Handle exception
}
}
Once the line is reached with locator.GetPositionAsync(), it stops. No exception is thrown, also the PositionError isn't raised.
I have no idea why, but in the beginning it worked once, never worked after that.
The location settings in de Android Emulator are as follow:
Based on my research, you did not acheved that Location Changes like this link
I wrote a demo about Location changes. This is running screenshot.
This is my demo
https://github.com/851265601/GeolocationDemo
I want create a tcp server in c# and use it in universal app javascript based project, and I create the folowing code (Server):
//C# Windows Runtime Component
public sealed class Server
{
public Server()
{
Debug.WriteLine("Server...");
}
public async void Connection()
{
IPAddress ip = IPAddress.Parse("192.168.0.10");
TcpListener server = new TcpListener(ip, portNumber);
TcpClient client = default(TcpClient);
try
{
server.Start();
Debug.WriteLine("Server started ... " + ip.ToString());
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
while (true)
{
client = await server.AcceptTcpClientAsync();
byte[] recievedBuffer = new byte[100];
NetworkStream stream = client.GetStream();
stream.Read(recievedBuffer, 0, recievedBuffer.Length);
string msg = Encoding.UTF8.GetString(recievedBuffer, 0, recievedBuffer.Length);
Debug.WriteLine(msg);
}
}
}
//in HTML
<script>
console.log("test");
var server = new Server.Server();
server.connection();
console.log("msg");
</script>
I don't know why Debug.WriteLine and console.log method don't work, nothing are printed in output or in javascript console.
The Server code works with Android client, if the server is "Console App" project but in "Universal App Javscript" nothing append, I don't have warning or error.
So I don't know if I'm doing bad, because console.log and Debug.WriteLine don't work.
I have a solution that work with windows universal app, I remove Connection and add followings methods:
public async void StartServer()
{
try
{
var streamSocketListener = new Windows.Networking.Sockets.StreamSocketListener();
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
await streamSocketListener.BindEndpointAsync(new HostName("192.168.0.10"), PortNumber);
}
catch (Exception ex){}
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
sender.Dispose();
}
//in main.js just call this method
new Server.Server().startServer();
But I still don't know why Debug.WriteLine() in c# and console.log() in javascript don't work.
I have the following scenario I think I'm doing it wrong.
I have a RootDialog which calls a ResultDialog. The ResultDialog presents the user a list of results (using HeroCard).
The ResultDialog closes itself using context.Done(..) after the message was posted.
In the RootDialog- AfterResultDialog Resume handler I want to ask the user if he has found the matching result, using another dialog (NotificationDialog), but I want to do that after 30 seconds.
After some research, this seems like it must be done using proactive messages.
It this example, I found a way to post the NotificationDialog in a proactive way.
private async Task AfterResultDialog(IDialogContext context, IAwaitable<object> result)
{
var message = await result as IMessageActivity;
var conversationReference = context.Activity.ToConversationReference();
ConversationStarter.conversationReference = JsonConvert.SerializeObject(conversationReference);
t = new Timer(timerEvent);
t.Change(30000, Timeout.Infinite);
context.Wait<string>(NotificationDialogAfter);
}
public void timerEvent(object target)
{
t.Dispose();
ConversationStarter.Resume();
}
But the problem I have is that I'm interested in the result of this NotifcationDialog to know what the user wants to do next.
But all examples I found using proactive-messages do not regard the result of a proactive message with dialog:
public class ConversationStarter
{
public static string conversationReference;
public static async Task Resume()
{
var message = JsonConvert.DeserializeObject<ConversationReference>(conversationReference).GetPostToBotMessage();
var client = new ConnectorClient(new Uri(message.ServiceUrl));
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(CancellationToken.None);
var task = scope.Resolve<IDialogTask>();
// here it seems only to be possible to call a dialog using Void
var dialog = new NotificationDialog();
task.Call(dialog.Void<object, IMessageActivity>(), null);
await task.PollAsync(CancellationToken.None);
await botData.FlushAsync(CancellationToken.None);
}
}
}
The NotificationDialogAfter handler should decide based on the user input which dialog to call next:
private async Task NotificationDialogAfter(IDialogContext context, IAwaitable<string> result)
{
var whereToContinue = await result;
if (whereToContinue.Equals("Start over"))
{
context.ClearAllConversationDataKeys();
context.Call(new TagDialog(), this.TagDialogAfter);
}
else if (whereToContinue == "Tell friends")
{
context.Call(new TellFriendsDialog(), TellFriendsDialogAfter);
}
else if (whereToContinue == "Feedback")
{
context.Call(new FeedbackDialog(), this.FeedbackDialogAfter);
}
}
So what I basically want is that the result of the NotificationDialog is forwarded to the NotificationDialogAfter handler which the Root dialog is waiting for.
Is this even possible?
I solve the problem by defining static continue handlers (in GlobalContinueHandler), that I can provide inside the NotificationDialog, when calling other dialogs.
[Serializable]
public class NotificationDialog : IDialog<string>
{
public Task StartAsync(IDialogContext context)
{
PromptDialog.Choice(context, Resume, new List<string> { "yes", "no" },
"Found what you're looking for?");
return Task.CompletedTask;
}
private async Task Resume(IDialogContext context, IAwaitable<string> result)
{
var message = await result;
if (message == "yes")
{
context.Call(new SignupDialog(), GlobalContinueHandler.SignupDialogAfter);
}
else
{
context.Call(new FeedbackDialog(), GlobalContinueHandler.FeedbackDialogAfter);
}
}
}
I'm really not fan of this solution but for now it seems to work.