UserDialogs Loading does not show up - c#

I am trying to see Loading progress as follows, but it does not show up.
View.cs
ViewModel.SelectedCommand.Execute(null);
ViewModel.cs
public ICommand SelectedCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following does not show loading
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classroomService.GetClassRoomAsync(SelectedClassroom.Id);
ObservableCollection<ClassroomViewModel> class = new ObservableCollection<ClassroomViewModel>(task.ConvertAll(x => new ClassViewModel(x)));
}
});
}
}
Another example
public ICommand ReloadCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
await RefreshList();
});
}
}
// the following also does not show loading
private async Task RefreshList()
{
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classService.GetClasses();
}
}

If you are using Acr.MvvmCross.Plugins.UserDialogs see that it's depreated and you should use directly Acr.UserDialogs.
Check if you have correctly initialized it as follows:
You have to register it in App.cs of your PCL project:
Mvx.RegisterSingleton<IUserDialogs>(() => UserDialogs.Instance);
And init from the android platform project in your main activity:
UserDialogs.Init(() => Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity)
Another thing to take into account is that you should inject it in your constructor as an IUserDialogs (you can use the static Instance way but it adds more flexibility and it is more testable by injecting it):
private readonly IUserDialogs _dialogs;
public ProgressViewModel(IUserDialogs dialogs)
{
this._dialogs = dialogs;
}
and use it like
private async Task RefreshList()
{
using (this._dialogs.Loading("Loading..."))
{
try
{
var task = await this._classService.GetClasses();
}
catch(Exception exc)
{
// This is done only for debugging to check if here lies the problem
throw exc;
}
}
}
You can check if it is properly working by calling something like
public ICommand MyTestCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following should should Loading for 3 seconds
using (this._dialogs.Loading("Loading..."))
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
});
}
}
HIH

I dont like this approuch but it works
Device.BeginInvokeOnMainThread(async () =>
{
try
{
using (UserDialogs.Instance.Loading(("Loading...")))
{
await Task.Delay(300);
await _syncController.SyncData();
//Your Service code
}
}
catch (Exception ex)
{
var val = ex.Message;
UserDialogs.Instance.Alert("Test", val.ToString(), "Ok");
}
});

Related

ChatMessages.Add(message) is not hit

I'm getting started with simple signalR application. When user puts his "name" and "room". It is sent to my hub
//This is Index page
public async Task<ActionResult> OnPost()
{
UserConnection userConnection = new()
{
Name = UserInput,
Room = JoinRoomInput
};
await _hubConnection.SendAsync("JoinRoom", userConnection);
return RedirectToPage("ChatGroup");
}
my chat looks like this
public class ChatHub : Hub<IChatClient>
{
private readonly string _botUser;
public ChatHub()
{
_botUser = "MyChat Bot";
}
public async Task JoinRoom(UserConnection userConnection)
{
await Groups.AddToGroupAsync(Context.ConnectionId, userConnection.Room);
await Clients.Group(userConnection.Room).ReceiveMessage(_botUser, $"{userConnection.Name} has joined {userConnection.Room}");
}
public async Task SendMessage(UserConnection userConnection, string message)
{
await Clients.Group(userConnection.Room).ReceiveMessage($"{userConnection.Name} : ", message);
}
}
So then, the use posts his details and hit joinroom. He is redirected to another razor page called Chatgroup.In this page, He should get the message as defined in "JoinRoom" in ChatHub.
//This is Chatgroup page
private readonly HubConnection _hubConnection;
public List<string> ChatMessages { get; set; }
public ChatGroupModel(HubConnection hubConnection)
{
_hubConnection = hubConnection;
}
public void OnGet()
{
_hubConnection.On<string>("ReceiveMessage", (message) => //Breakpoint hits here
{
ChatMessages.Add(message); // When placed a breakpoint here, It is skipped.
});
}
I'm getting ChatMessages is Null error.
So my question is how will my client side code _hubConnection.On<> get the response from chathub?
I think this is the issue.
I've registered my signalr in startup class like this
services.AddTransient<HubConnection>((ChatClient) => {
var hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:44389/chathub").Build();
hubConnection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await hubConnection.StartAsync();
};
hubConnection.StartAsync();
return hubConnection;
});
I get a null exception error in my razor page. I think it's because _hubConnection.On<>... skips chatmessage.add.
#foreach(var messages in Model.ChatMessages) //ChatMessages is Null
{
<div>
#messages
</div>
}

Throwing FormCancelledException from await instead of AggregateException in unit test

I'm currently trying to test the following code in an application that makes use of the Microsoft Bot Framework.
public async Task ResumeAfterCalculation_v2FormDialog(IDialogContext context, IAwaitable<Calculation_v2Form> result)
{
try
{
var extractedCalculationForm = await result;
//Removed additional code
}
catch (FormCanceledException ex)
{
var reply = "You have canceled the operation.";
await _chat.PostAsync(context, reply);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
context.Done<object>(null);
}
}
When a user types 'quit' to the bot the 'await result' code throws a FormCanceledException and the code quits the form.
When creating a test I implemented a class to mock the IAwaitable:
public class TaskAwaiterHelper<T> : IAwaiter<T>, IAwaitable<T>
{
public Task<T> Task { get; }
public TaskAwaiterHelper(T obj)
{
this.Task = System.Threading.Tasks.Task.FromResult(obj);
}
public TaskAwaiterHelper(Task<T> task)
{
this.Task = task;
}
public bool IsCompleted { get { return Task.IsCompleted; } }
public void OnCompleted(Action action)
{
SynchronizationContext context = SynchronizationContext.Current;
TaskScheduler scheduler = context == null ? TaskScheduler.Current
: TaskScheduler.FromCurrentSynchronizationContext();
Task.ContinueWith(ignored => action(), scheduler);
}
public T GetResult()
{
return Task.Result;
}
public IAwaiter<T> GetAwaiter()
{
return this;
}
}
I then created the following test:
[Fact]
public async Task ResumeAfterCalculation_v2FormDialog_WasCancelled_ThenCallsDone()
{
//Arrange
var chat = new Mock<IChatHelper>();
var calculationApi = new Mock<ICalculationApi>();
var dialogContextMock = new Mock<IDialogContext>();
var rootLuisDialog = new RootLuisDialog(chat.Object, calculationApi.Object);
var taskAwaiter = new TaskAwaiterHelper<Calculation_v2Form>(new Task<Calculation_v2Form>(() =>
{
throw new FormCanceledException("Error created for test test", null);
}));
taskAwaiter.Task.Start();
//Act
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, taskAwaiter);
//Assert
chat.Verify(c => c.PostAsync(dialogContextMock.Object, "You have canceled the operation."), Times.Once());
dialogContextMock.Verify(t => t.Done<object>(null), Times.Once());
}
Now whatever I try to do I the exception that's being thrown in the IAwaitable is being wrapped in an AggregateException, so we always end up in the catch (Exception ex) instead of the desired catch (FormCanceledException ex)
Is there a way to make a Task throw a specific Exception instead of an AggregateException (I mean there should be as the bot framework itself seems to be able to do it).
I just found the answer, I basically created a new class:
public class ExceptionThrower : IAwaitable<Calculation_v2Form>
{
public IAwaiter<Calculation_v2Form> GetAwaiter()
{
throw new FormCanceledException("Error created for test test", null);
}
}
And just provided this to the method:
var exceptionThrower = new ExceptionThrower();
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, exceptionThrower);

UserDialogs loading does not pop up

I wonder what I am doing wrong in the following implementation.
I cannot able to see loading dialog, even to opening the ClassroomViewModel takes few seconds.
public IMvxCommand ClassroomSelectedCommand => new MvxAsyncCommand<ClassroomViewModel>(ClassroomSelected);
private async Task ClassroomSelected(Model obj)
{
using (UserDialogs.Instance.Loading("Loading..."))
{
try
{
ShowViewModel<ClassroomViewModel>(new { Id = obj.Id });
}
catch (Exception ex)
{
}
}
}
You are using async APIs, use an MvxAsynCommand
private IMvxAsynCommand _classroomSelectedCommand;
public IMvxAsynCommand ClassroomSelectedCommand => _classroomSelectedCommand ?? (_classroomSelectedCommand = new MvxAsyncCommand<ClassroomViewModel>(ClassroomSelectedAsync));
private async Task ClassroomSelectedAsync(Model obj)
{
using (UserDialogs.Instance.Loading("Loading..."))
{
await Task.Delay(300);
try
{
ShowViewModel<ClassroomViewModel>(new { Id = obj.Id });
}
catch (Exception ex)
{
}
}
}

Xamarin retry failed data request

Retry action Image
I am building a Xamarin iOS & android App, and I want to implement a retry function to all the failed webcall or in case of disconnection, I already use Polly in the BLL side, and I want to give the user the possibility to retry manually as shown on the above image.
protected List<Task> _taskList;
_taskList.Add(Task.Run(async () =>
{
try
{
**// Webservice Call**
Task<UtilisateurDTO> utilisateurTask = UserFactory.Login(username, pwd,
App.Hardware.GetDeviceId());
UtilisateurDTO utilisateur = await utilisateurTask;
if (utilisateur != null)
{
InvokeOnMainThread(() =>
{
**// Set result to ui component**
});
}
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, task => task.Start());
});
_snackBar.Show();
});
}
}));
I know that it's not possible to retry completed tasks, and I can't call my web service outside a task (to not block the UI thread), so what's the alternative?
Update with a solution
If you want to handle exceptions and retry in one place, here is my solution (not the best cause it reload everything)
// BaseViewClass
public abstract class BaseViewController:UIViewController
{
// Function to override in child controllers
protected abstract void ReloadData(TTGSnackbar obj);
public void HandleExceptions(Exception e)
{
// On commence par cacher la ProgressBar
InvokeOnMainThread(HideLoadigProgressBar);
if (e is ConnectionLostException)
{
Console.WriteLine("ConnectionLostException: " + e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("Connection lost !", TTGSnackbarDuration.Forever, "retry", ReloadData);
_snackBar.Show();
});
}
else if (e is TimeoutException)
{
Console.WriteLine("TimeoutException: "+ e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("TimeoutException", TTGSnackbarDuration.Forever, "Retry", ReloadData);
_snackBar.Show();
});
}
.....................
}
// Other ViewController
public partial class HomeController : BaseViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
LoadData();
}
public void LoadData(){
Task.Run(async () =>
{
try
{
// Web calls
Task<UtilisateurDTO> getUserTask = AccueilFactory.GetUser();
UtilisateurDTO utilisateur = await getUserTask;
// Set UI result
}
catch(Exception ex) {
HandleExceptions(ex);
}
}
}
protected override void ReloadData(TTGSnackbar obj)
{
LoadData();
}
}
You have to call your function again on exception, not only rerun last task.
Your code will be like this:
private void Login()
{
try
{
LoginInner(); // here you call service and update UI
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, LoginInner); // ** call again loginInner **
});
_snackBar.Show();
});
}
}

What prevents this task from being long running?

For testing purposes, I am using this directly inside of a razor block in a .cshtml page.
#functions{
public class Inline
{
public HttpResponseBase r { get; set; }
public int Id { get; set; }
public List<System.Threading.Tasks.Task> tasks = new List<System.Threading.Tasks.Task>();
public void Writer(HttpResponseBase response)
{
this.r = response;
tasks.Add(System.Threading.Tasks.Task.Factory.StartNew(
() =>
{
while (true)
{
r.Write("<span>Hello</span>");
System.Threading.Thread.Sleep(1000);
}
}
));
}
}
}
#{
var inL = new Inline();
inL.Writer(Response);
}
I had expected it to write a span with the text "Hello" once every second. It will write "Hello" once sometimes, but not every time or even most times. Why isn't this task long running?
The reason you are seeing different result is because the task is running asynchronously and if the response object is completed before your task gets a chance to write on it, the taks will throw exception and it will terminate the only way you can do this is if you add Task.WaitAll() at the end of the Writer() method.
This will work but the page will not stop loading content.
this.r = response;
tasks.Add(System.Threading.Tasks.Task.Factory.StartNew(
() =>
{
while (true)
{
r.Write("<span>Hello</span>");
r.Flush(); // this will send each write to the browser
System.Threading.Thread.Sleep(1000);
}
}
));
//this will make sure that the response will stay open
System.Threading.Tasks.Task.WaitAll(tasks.ToArray());
Here is another option this one uses a custom ActionResult , it first process the controller (the default result) after that is done it starts the task.
public class CustomActionResult:ViewResult
{
public override void ExecuteResult(ControllerContext context)
{
base.ExecuteResult(context);
var t = Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep(1000);
context.HttpContext.Response.Write("<h1>hello</h1>");
context.HttpContext.Response.Flush();
}
});
Task.WaitAll(t);
}
}
In your controller
public class HomeController : Controller
{
public ActionResult Index()
{
return new CustomActionResult();
}
}

Categories

Resources