UI Thread issue with view model in MVVMCross - mvvm

I am using MVVMCross with my cross-platform Windows Phone and Android app. In the core project's main view model, I am doing some background work using TPL and I want to make sure that in the callback, when I make changes to the properties of the view model which will trigger UI change, that the code is run on UI thread, how do I achieve this?
For code, here is how it likes
private MvxGeoLocation _currentLocation;
private Task<MvxGeoLocation> GetCurrentLocation()
{
return Task.Factory.StartNew(() =>
{
while (_currentLocation == null && !LocationRetrievalFailed)
{
}
return _currentLocation;
});
}
var location = await GetCurrentLocation();
if (LocationRetrievalFailed)
{
if (location == null)
{
ReverseGeocodingRequestFailed = true;
return;
}
// Show toast saying that we are using the last known location
}
Address = await GooglePlaceApiClient.ReverseGeocoding(location);

Did you try IMvxMainThreadDispatcher?
var dispatcher = Mvx.Resolve<IMvxMainThreadDispatcher>();
dispatcher.RequestMainThreadAction(()=> { .... });
See more on the implementation:
https://github.com/MvvmCross/MvvmCross/search?q=IMvxMainThreadDispatcher&type=Code
Usually I don't think you need this though.
Since you start the async processing from main thread, the async operations should return back to main thread.
Can you give an example of the async code you are doing?

Update on 24th August 2020:
As #claudio-redi has mentioned, ExecuteOnMainThreadAsync needs to be used. But Mvx.Resolve is now obsolete. So the latest snippet would be:
var mainThreadAsyncDispatcher = Mvx.IoCProvider.Resolve<IMvxMainThreadAsyncDispatcher>();
await mainThreadAsyncDispatcher.ExecuteOnMainThreadAsync( async ()=> { await SomeAsyncTask() });

Method RequestMainThreadAction is now obsolete. Today you have to do
var dispatcher = Mvx.Resolve<IMvxMainThreadAsyncDispatcher>();
await dispatcher.ExecuteOnMainThreadAsync(()=> { .... });

Related

How to listen onsnap changes firebase flutter

I want to use firebase snapshot doc changes and when doc change get popup screen. im using mvvp architecture with riverpod. how can i implement it repository and show in my relevant screen?
1 . repository --- 2. ModelView --- 3. screen( homeview with return autotab router.)
currently what i doing is call getorder inside widget and call popup inside modified field.
this is my repository.
class OrderOnSnap {
final Reader read;
OrderOnSnap(
this.read,
);
late final _firestore = read(firestoreProvider);
getOrders(uid) async {
_firestore.collection("orders_ready").snapshots().listen((result) {
for (var res in result.docChanges) {
if (res.type == DocumentChangeType.added) {
//print("added");
//print(res.doc.data());
} else if (res.type == DocumentChangeType.modified) {
print("modified");
final abc = res.doc.data();
final status = abc!['orderPickup'];
if (status == '2') {
print('new order received');
popupscreen(){};
}
} else if (res.type == DocumentChangeType.removed) {
print("removed");
}
}
});
}
}
The firebase event is an external event that your application receives and it is interpreted in order to execute an action. From this perspective it is just like an event that a GUI sends. You need a very similar code structure. You have a listener that listens to the event, code that interpretes it and triggers an action on the presenter (or the controller in mvc). The presenter can then call an interactor or in your case just update the model and tell the 'popup' view to open.

Blazor WASM Load Data before Render Page

I would like to load some Data before I Render my Blazor Application because in depndency to the loaded data I would like to render my app (layout, navbar ...)
Now I want to use the OnInitialised method instead of OnInitialisedAsync and with no async and await keywords.
But now I had a problem to convert the data which I get back from my API.
protected override void OnInitialized()
{
try
{ Console.WriteLine("Test1Mainasync");
LoadCategories();
}
catch (Exception e)
{
jsRuntime.ToastrError(e.Message);
}
}
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
SD.Categories = CategoriesInit1.ToList();
//foreach(var categorie in CategoriesInit){
// SD.Categories.Append(categorie);
//}
Console.WriteLine("Test1Main");
}
Has someone an idea why this converting issues happen?
I think you have this method:
public async Task<IEnumerable<CategorieDTO>> GetAllCategories()
and you should call it this way:
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
and:
protected override async Task OnInitializedAsync()
{
try
{ Console.WriteLine("Test1Mainasync");
await LoadCategories();
}
Has someone an idea why this converting issues happen?
In your code CatagiesInit1 is a Task, it's not a List<CategorieDTO>. You only get the List<CategorieDTO> when the task completes which you have no control over as you don't await the completion of the Task. In all likelyhood, your sync code will run to completion before that happens.
If your CategoryService returns a Task then the code that handles it must be async code. You can't escape from the async world back into the sync world without consequencies. If you want to live in the sync world then all the data pipeline code also needs to be blocking sync code.
If I understand your comments correctly, you want nothing to render until a certain set of conditions are met. If so add some standard Loading... component code to the page if it's page specific or App.razor if it's on initial load, or say MainLayout if it's application wide.
Here's a quick an dirty example:
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
#if (Loaded)
{
<RouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
<FocusOnNavigate RouteData="#routeData" Selector="h1" />
}
else
{
<div class="m-2 p-5 bg-secondary text-white">
<h3>Loading.....</h3>
</div>
}
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="#typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
#code {
private bool Loaded;
protected override async Task OnInitializedAsync()
{
Loaded = false;
// simulate getting the data first
await Task.Delay(5000);
Loaded = true;
}
}
Your call to API endpoint return an awaitable task but not the IEnumerable, So you can not assign awaitable task to IEnumerable so this piece of code wont work
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
}
You should have your LoadCategories function like this
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
}
API calls should be awaitable, else it will stuck your UI
You can use this solution as well
private void LoadCategories()
{
var t = Task.Run(() => categorieService.GetAllCategories()()).GetAwaiter();
t.OnCompleted(() =>
{
CategoriesInit1 = t.GetResult();
// you may need to call statehaschanged as well
StateHasChanged();
});
}

Blazor Server EF Core Cancelation

In my app I have to be able to cancel uploads, I tried with threads cancelation but nothing happens, I think it is because I use DBContextFactory and I create a context for each uploaded file.
So I did this to save files in DB:
private async Task OnFilesDropped(FileUploadModel upload)
{
Uploads.Add(upload);
if (string.IsNullOrEmpty(upload.Error))
{
using var context = DbFactory.CreateDbContext();
upload.Context = context;
context.FileUploads.Add(upload);
upload.UploadCompletion = 45;
await context.SaveChangesAsync();
upload.UploadCompletion = 100;
}
}
and this in case of deleting a uploaded/uploading file:
private async Task DeleteUpload(FileUploadModel upload)
{
Uploads.Remove(upload);
await UploadsChanged.InvokeAsync(Uploads);
if (string.IsNullOrEmpty(upload.Error))
{
if (upload.UploadCompletion != 100)
{
await upload.Context.DisposeAsync();
}
else
{
using var context = DbFactory.CreateDbContext();
context.FileUploads.Remove(upload);
await context.SaveChangesAsync();
}
}
}
This way works because I dispose of the context, but I wonder if there is a better way of doing this? or if this solution could be problematic somehow?
Best Regards.
You should use a CancellationToken.
The SaveChangesAsync method on your context has an overload that can be provided a cancellationToken.
await context.SaveChangesAsync(cancellationToken);
If you already have a CancellationToken higher in the call stack, you can just pass that one down. Otherwise, you can create a CancellationTokenSource and use that to generate a cancellation token and then cancel it when appropriate.

Correct way to call async methods from within a data-bound property setter?

Now I know properties do not support async/await for good reasons. But sometimes you need to kick off some additional background processing from a property setter - a good example is data binding in a MVVM scenario.
In my case, I have a property that is bound to the SelectedItem of a ListView. Of course I immediately set the new value to the backing field and the main work of the property is done. But the change of the selected item in the UI needs also to trigger a REST service call to get some new data based on the now selected item.
So I need to call an async method. I can't await it, obviously, but I also do not want to fire and forget the call as I could miss exceptions during the async processing.
Now my take is the following:
private Feed selectedFeed;
public Feed SelectedFeed
{
get
{
return this.selectedFeed;
}
set
{
if (this.selectedFeed != value)
{
this.selectedFeed = value;
RaisePropertyChanged();
Task task = GetFeedArticles(value.Id);
task.ContinueWith(t =>
{
if (t.Status != TaskStatus.RanToCompletion)
{
MessengerInstance.Send<string>("Error description", "DisplayErrorNotification");
}
});
}
}
}
Ok so besides the fact I could move out the handling from the setter to a synchronous method, is this the correct way to handle such a scenario? Is there a better, less cluttered solution I do not see?
Would be very interested to see some other takes on this problem. I'm a bit curious that I was not able to find any other discussions on this concrete topic as it seems very common to me in MVVM apps that make heavy use of databinding.
I have a NotifyTaskCompletion type in my AsyncEx library that is essentially an INotifyPropertyChanged wrapper for Task/Task<T>. AFAIK there is very little information currently available on async combined with MVVM, so let me know if you find any other approaches.
Anyway, the NotifyTaskCompletion approach works best if your tasks return their results. I.e., from your current code sample it looks like GetFeedArticles is setting data-bound properties as a side effect instead of returning the articles. If you make this return Task<T> instead, you can end up with code like this:
private Feed selectedFeed;
public Feed SelectedFeed
{
get
{
return this.selectedFeed;
}
set
{
if (this.selectedFeed == value)
return;
this.selectedFeed = value;
RaisePropertyChanged();
Articles = NotifyTaskCompletion.Create(GetFeedArticlesAsync(value.Id));
}
}
private INotifyTaskCompletion<List<Article>> articles;
public INotifyTaskCompletion<List<Article>> Articles
{
get { return this.articles; }
set
{
if (this.articles == value)
return;
this.articles = value;
RaisePropertyChanged();
}
}
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
...
}
Then your databinding can use Articles.Result to get to the resulting collection (which is null until GetFeedArticlesAsync completes). You can use NotifyTaskCompletion "out of the box" to data-bind to errors as well (e.g., Articles.ErrorMessage) and it has a few boolean convenience properties (IsSuccessfullyCompleted, IsFaulted) to handle visibility toggles.
Note that this will correctly handle operations completing out of order. Since Articles actually represents the asynchronous operation itself (instead of the results directly), it is updated immediately when a new operation is started. So you'll never see out-of-date results.
You don't have to use data binding for your error handling. You can make whatever semantics you want by modifying the GetFeedArticlesAsync; for example, to handle exceptions by passing them to your MessengerInstance:
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
try
{
...
}
catch (Exception ex)
{
MessengerInstance.Send<string>("Error description", "DisplayErrorNotification");
return null;
}
}
Similarly, there's no notion of automatic cancellation built-in, but again it's easy to add to GetFeedArticlesAsync:
private CancellationTokenSource getFeedArticlesCts;
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
if (getFeedArticlesCts != null)
getFeedArticlesCts.Cancel();
using (getFeedArticlesCts = new CancellationTokenSource())
{
...
}
}
This is an area of current development, so please do make improvements or API suggestions!
public class AsyncRunner
{
public static void Run(Task task, Action<Task> onError = null)
{
if (onError == null)
{
task.ContinueWith((task1, o) => { }, TaskContinuationOptions.OnlyOnFaulted);
}
else
{
task.ContinueWith(onError, TaskContinuationOptions.OnlyOnFaulted);
}
}
}
Usage within the property
private NavigationMenuItem _selectedMenuItem;
public NavigationMenuItem SelectedMenuItem
{
get { return _selectedMenuItem; }
set
{
_selectedMenuItem = val;
AsyncRunner.Run(NavigateToMenuAsync(_selectedMenuItem));
}
}
private async Task NavigateToMenuAsync(NavigationMenuItem newNavigationMenu)
{
//call async tasks...
}

Async CSLA Calls

The 'standard' CSLA async server calls have typically been structured per the following:
Base class:
public static void GetMyObject(EventHandler<DataPortalResult<MyObject>> callback) {
var dp = new DataPortal<MyObject>();
dp.FetchCompleted += callback;
dp.BeginFetch();
}
ViewModel:
protected override void OnInitialize(object parameter) {
base.OnInitialize(parameter);
base.IsBusy = true;
MyObject.GetMyObject((o, e) => {
if (HasNoException(e)) {
Model = e.Object;
}
base.IsBusy = false;
});
}
With the new async/await features, the format would be something like this:
public async static Task<MyObject> GetMyObject() {
return await DataPortal.FetchAsync<MyObject>();
}
and
protected async override void OnInitialize(object parameter) {
base.OnInitialize(parameter);
base.IsBusy = true;
Model = await MyObject.GetMyObjectAsync();
base.IsBusy = false;
}
Should the callback pattern be considered deprecated at this point, or is it still useful for certain UI technologies? When doing a new project, I'd rather not have those methods in there if I can help it.
Personally, I prefer TAP methods over callbacks in all my code.
With Microsoft.Bcl.Async, most platforms support async. However, there are a few situations where neither TAP nor Task is available, e.g., Windows Phone 7.0, .NET CF, SL 3. I would only use callbacks if I had to support one of those platforms.