Multiple alerts with messagingcenter subscription

CaioshinCaioshin ITMember ✭✭
edited July 2015 in Xamarin.Forms

Hello,
I've a simple questione that I was unable to solve until now.
I've a viewmodel that try to load a list and a view that use it as bindingcontext.
I want that when the viewmodel fails to load the list simply display an alert and return to the previous page.

To do it I'm doing the following:

1-I subscribe my view to the message so it can show a popup when it receives a certain message:

protected override void OnAppearing()
{
if (!Helpers.IsRegistered)
{
MessagingCenter.Subscribe<ViewModelBase, string[]>(this, Helpers.POPUP_PROBLEM_OPEN_NEWS, (sender, values) =>
{
Helpers.IsRegistered = true;
Device.BeginInvokeOnMainThread(() =>
{
DisplayAlert(values[0], values[1], values[2]);
if (Navigation.NavigationStack.Count > 0)
Navigation.PopAsync();
});
});
}

        base.OnAppearing();

    }

2-when I have a problem in my viewmodel loading the list I send a message in this way (I putted all in an external helper class)

MessagingCenter.Send<ViewModelBase, string[]>(pSender, messageID, values);

I don't know why but the problem seems to be that my view is registered multiple times to the message: in fact it shows me the alert 2 times on the first execution, and then it increase exponentially evety time I reopen it.

I tried to use an external variable that tells me when I already have registered the view to this message and also to unregister them on OnDisappearing event, in this way:

protected override void OnDisappearing()
{
base.OnDisappearing();

        MessagingCenter.Unsubscribe<ViewModelBase>(this, Helpers.POPUP_PROBLEM_OPEN_NEWS);
        ((NewsViewModel)this.BindingContext).Registered = false;
    }

but it doesn't solve my problem.
Can somebody help me to understand what's wrong?

Thank you

Posts

  • adamkempadamkemp USInsider, Developer Group Leader mod

    If you set a breakpoint in OnAppearing can you see it being called twice for a single appearance?

  • CaioshinCaioshin ITMember ✭✭

    No, it's called only 1 time, and from the second time I enter in this view the messagingcenter.subscribe is not called due to a bool variable that I use to remember if it's already registered.
    Could it be a problem related to the unregister process?

    Another things that could be the cause of the problem is that I want to show a popup and immediately return back on navigation. So inside the callback of my subcribe method I want to execute the displayalert and navigate back:

    Device.BeginInvokeOnMainThread(() =>
                        {
                            DisplayAlert(values[0], values[1], values[2]);
                            if (Navigation.NavigationStack.Count > 0)
                                Navigation.PopAsync();
                        });
    
  • Xami3Xami3 PKMember ✭✭✭
    edited July 2015

    @Caioshin

    i think its better to make a test project and upload it here, because we don't know how are you handling the vm etc...

    i noticed few things while working with xamarin.forms, i think you should consider these changes as well.
    1. i got Java.null pointer exception many times while getting vm from binding context, its better to make a private field and use that through out the page...
    2.i do unsubscribe before subscribing messaging center this way i don't need any kind of flag
    3. on call back of the subscription i unsubscribe messaging center as well (Depends on Application Flow and logic)

  • CaioshinCaioshin ITMember ✭✭
    edited July 2015

    Ok, I made a project as example that executes the task as in my original project.
    I think we have a couple of problems here that probably somebody can address me to solve in best way:

    1. The message is sent when the viewmodel execute a task (in real life when trying to load something from a webservice there's an error) but at this time I think that none is still registered (the view register itself inside the onAppearing method)
    2. Inside the callback of the subscribe method I execute a popasync, but NavigationStack is empty, probably because in the onAppearing method the navigation hasn't already added the page?

    From the second time you press the button the displayAlert is displayed but in increasing times as in my project

    Thank you

  • ylemsoulylemsoul RUMember ✭✭✭

    @Caioshin, your sample differs from the original post ( In the sample you don't unregister), but regarding your sample key things are:
    1) you're sending message in the VM constructor. VM instantiated in the constructor of the NextPage, so at the first time you send the message NextPage is not even constructed (and not pushed) yet.
    2) you don't unregister (as mentioned above) so the second time you send the message old instance of the NextPage receives the message. It shows alert, but his NavigationStack is empty (because it leaked actually and not in the navigation stack).

  • CaioshinCaioshin ITMember ✭✭

    Thank you,yes you're right,in my original project I unsubscribe inside onDisappear event but the problem is the same.
    So keeping in mind that in my viewmodel I try to consume a web service to populate a list,where is the right place to do the task?

  • Xami3Xami3 PKMember ✭✭✭

    @Caioshin

    Create separate class say "SyncManager", there do all ur server side coding like Tasks for downloading/uploading etc ... and in Your vm await on these tasks...

  • CaioshinCaioshin ITMember ✭✭

    Ok but this don't solve the problem that the view is not ready yet to receive a message from messaging enter: Supposing that I execute the async load awaiting an external method, it's possible that this method immediately return back due to some error (ie if it can resolve the remote host). In this case I should still have the problem because my vm sends a message before the view has finished to appear (like now).

  • CaioshinCaioshin ITMember ✭✭

    While I'm trying to solve the problem about navigation back that occurs before the page is completely pushed into the navigationstack, I found that the problem because I see multiple popups is not due to an excess of subscriptions but to the completed event handler that is called multiple times.
    The strange is that the method that assign the handler do it assuring that only 1 handler exists at any time, and is called only 1 time:

      private void OnUseWebService()
            {
                Loading = true;
    
                Helpers.ClientWS.NewsCompleted -= client_NewsCompleted;
                Helpers.ClientWS.NewsCompleted += client_NewsCompleted;
    
                Helpers.ClientWS.NewsAsync();
    
            }
    

    Do you have an idea about?

  • ChristineCantChristineCant GBMember

    Thank you!!! The issue was driving me up the wall - I didn't realise that the object type that was being passed had to be the same in the unsubscribe so when I updated the object in the subscribe, I forgot to do it in the unsubscribe. I have looked at the spelling of the message name 1000 times without realising it was the object that was wrong! Thank you!

  • GVxGVx USMember ✭✭✭

    Thank you from me also.. Great to see that after the initial post (2 years ago) we all have a solution and better understanding on how this works... instead of hacks to get around it... ;)

  • @TonyCasey said:
    Oh my god, this one was a nightmare for me for weeks. I sat down then and spent two days trying to sort it.

    I hope this saves time for others..

    It turned out to be a simple solution, which I saw on a few threads. I paid attention to them and really tried but actually kept doing it wrong and then chasing other possibilities down wrong roads.

    Unsubscribing properly.

    I am sending a message with an object from the Droid app into a FreshMvvm ViewModel in the PCL project....

    Droid..

    MessagingCenter.Send(MainActivity.PCLApp, "NewMessageNotificationReceived", notificationObj);

    Passing my object "notificationObj" to the MainActivity.PCLApp under the title of "NewMessageNotificationReceived"

    ViewModel

    MessagingCenter.Subscribe<App, NotificationObj>(App.Current, "NewMessageNotificationReceived", (sender, notificationObj ) =>

    Subscribing to the PCL App with arguments of type "NotificationObj"
    This works great and I never had a problem receiving.

    This is a chat app (think whatsapp), so when a topic is selected and the chat screen opens, I want to listen for any new messages that come in and show them immediately on the screen.

    Naturally, I will probably have a look at a number of different chats/topics, so each time I pick one, a new chat screen is being pushed into the stack and the listeners started. I am subscribing them in the Init()

    When a message did come in, the listener was being fired numerous times (the exact number of times I had opened a chat), I could see that there was a single, NOT multiple pages in the stack, so how was this happening? Somehow, the listener was remaining in memory even though the page was popped. Or it wasn't being unsubscribed properly.

    I tried making sure that I was unsubscribing the listeners, in different places, ViewDisappearing and even a custom navigation page and overriding the this.Popped += (object sender, NavigationEventArgs e) method as suggested by @Caioshin above.

    I came back a few times and made sure that I was writing the Unsubscribe part right, tried a few variations and it was still happening.

    Until finally...

    MessagingCenter.Unsubscribe<App, Notification>(App.Current, "NewMessageNotificationReceived");

    Is the correct way for me to Unsubscribe from the initial subscribe above.

    I am unsubscribing in the ViewDisappearing event of the same page and it works well.
    Thank god, this was a huge one for me.

    Make sure your unsubscribe matches the origin, the args and the name and check it a few times.

    This works for me.

  • RavinderJangraRavinderJangra USMember ✭✭✭

    you should always unsubscribe messaging center if u subscribe. you can do so in OnDisappear()

Sign In or Register to comment.