0

I have a UWP project and I want to message passing between ViewModel and View with CommunityToolkit MVVM Toolkit.

As you can see in my ViewModel we have something like below

        private async void CallbackClick()
        {
            while (true)
            {
                CallbackDto callback = callbackQueue.Pop();
                if (callback is null)
                    break;

                callbackQueue.Push(new CallbackDto(callback.Number, DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
                string normalizedAddress = callback.Number;
                BSN.LinphoneSDK.Call outgoingCall = await LinphoneManager.Instance.NewOutgoingCall($"{normalizedAddress}");
                await outgoingCall.WhenEnded();
                // TODO: It is mandatory for backing to Dialer from InCall, but it is very bugous and must fix it
                await Task.Delay(1000);
                Task<CancellationToken> cancellationTokenTask = WeakReferenceMessenger.Default.Send<ContinueCallbackAnsweringRequestMessage>();
                CancellationToken cancellationToken = await cancellationTokenTask;
                if (cancellationToken.IsCancellationRequested)
                    break;
            }
        }

And I have register on message in Dialer.xaml.cs in NavigatedTo like below

 WeakReferenceMessenger.Default.Register<ContinueCallbackAnsweringRequestMessage>(this, async (r, message) =>
            {
                var continueCallbackAnsweringDialog = new MessageDialog("آیا مایل به ادامه پاسخ‌دهی به تماس‌های درخواستی هستید؟");
                TaskCompletionSource<CancellationToken> tcs = new TaskCompletionSource<CancellationToken>(TaskCreationOptions.RunContinuationsAsynchronously);
                continueCallbackAnsweringDialog.Commands.Add(new UICommand(
                    "بلی",
                    new UICommandInvokedHandler((IUICommand command) =>
                    {
                        tcs.SetResult(new CancellationToken(false));
                    })));
                continueCallbackAnsweringDialog.Commands.Add(new UICommand(
                    "خیر",
                    new UICommandInvokedHandler((IUICommand command) =>
                    {
                        tcs.SetResult(new CancellationToken(true));
                    })));
                continueCallbackAnsweringDialog.DefaultCommandIndex = 0;
                continueCallbackAnsweringDialog.CancelCommandIndex = 1;
                await continueCallbackAnsweringDialog.ShowAsync();

                message.Reply(tcs.Task);
            });

In this scenario I got a InvalidOperationException on WeakReferenceMessenger.Default.Send. My question is how to handle this situation?

In another side, if I remove async and await keyword in handler, my code is work correctly. but the problem is I do not await on IAsyncOperation and I want to await on in it.

I add discussion on CommunityToolkit GitHub about that.

Dharman
  • 30,962
  • 25
  • 85
  • 135
sorosh_sabz
  • 2,356
  • 2
  • 32
  • 53
  • No, my `ContinueCallbackAnsweringRequestMessage` is inherit from `RequestMessage` and I need for response of sending message to continue working – sorosh_sabz Jul 01 '22 at 12:34
  • Then pass the non-awaited task returned by `continueCallbackAnsweringDialog.ShowAsync` directly to `message.Reply`. – mm8 Jul 01 '22 at 12:40
  • It is not my desirable, because I want to return another value, as you can see and in general we want to run many async command in handler and I want to learn how to call multiple `await` in handler without any problem? – sorosh_sabz Jul 01 '22 at 12:42
  • The `MessageHandler` will return as soon as you await something so this won't work. – mm8 Jul 01 '22 at 12:49
  • But I need make many await command on it, How to achieve that? – sorosh_sabz Jul 01 '22 at 13:24

1 Answers1

0

My question is how to handle this situation?

Since the MessageHandler won't (can't) be awaited by the toolkit, it returns when you call await ....

You could try something like this:

WeakReferenceMessenger.Default.Register<ContinueCallbackAnsweringRequestMessage>(
    this, (r, m) => m.Reply(ShowDialogAndWaitForResult()));

...where ShowDialogAndWaitForResult() is a custom async method that returns a Task<T>, calls the ShowAsync() of the dialog and wait for the result.

Another option is implement your own blocking (non-async) dialog. Or consider another solution which don't involve a messenger.

mm8
  • 163,881
  • 10
  • 57
  • 88