0

I'm working on an application that should open a popup while processing some images. The problem is that the popup does not open. In some tests I've done, the popup only opens after all processing has completed, even then if I put Thread.Sleep() to wait before closing. I have the following code in my maui .net application. This code should open the popup at the beginning of image conversion processing. And then at the end it's called again to close the popup. This is the code for my popup controller.

WaitPopup.xaml

<mct:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:mct="clr-namespace:CommunityToolkit.Maui.Views;assembly=CommunityToolkit.Maui"
             xmlns:viewmodel="clr-namespace:ImageConvert.ViewModel"
             x:DataType="viewmodel:GifAnimationViewModel"
             x:Class="ImageConvert.Views.Popups.WaitPopup"
           CanBeDismissedByTappingOutsideOfPopup="False">
    <VerticalStackLayout>
        <Label 
            Text="Wait minute!"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />
        <Image Source="Resources/Images/loading.gif" IsAnimationPlaying="{Binding IsAnimationGif }" WidthRequest="150"/>
    </VerticalStackLayout>
</mct:Popup>

WaitPopup.xaml.cs

using CommunityToolkit.Maui.Views;

namespace ImageConvert.Views.Popups;

public partial class WaitPopup : Popup
{
    public WaitPopup()
    {
        InitializeComponent();
    }
}

This is the viewmodel code that does a pre-processing and sends a message to open the popup and calls the image processing service.

ConvertViewModel.cs

public ICommand ConverterCommand => new Command(ConverterImages);

private void ConverterImages(object obj)
{
    try
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var folder = string.Empty;

            switch (FormatSelected)
            {
                case "jpeg":
                    folder = FoldersNames.NamesFolders[0];
                    break;
                case "pdf":
                    folder = FoldersNames.NamesFolders[1];
                    break;
                case "png":
                    folder = FoldersNames.NamesFolders[2];
                    break;
                case "bmp":
                    folder = FoldersNames.NamesFolders[3];
                    break;
                case "tiff":
                    folder = FoldersNames.NamesFolders[4];
                    break;
                case "webp":
                    folder = FoldersNames.NamesFolders[5];
                    break;
                case "gif":
                    folder = FoldersNames.NamesFolders[6];
                    break;
                default:
                    folder = FoldersNames.NamesFolders[2];
                    break;
            }
            
            string folderSavePath = Path.Combine(FoldersNames.PathRoot, folder);
            var listPaths = (from i in ListImages select i.PathImage).ToList();

            var send = new SendMessageToDisplayWaitWindow()
            {
                OpenOrCloseDisplay = true,
                ListImages = listPaths,
                FolderSave = folderSavePath,
                FormatSelect = FormatSelected
            };
            WeakReferenceMessenger.Default.Send<SendMessageToDisplayWaitWindow>(send);
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine("***** " + ex.Message);
    }
}

Then the code comes to this part, where it should open the popup and call the image processing service.

Convert.xaml.cs

private IConverterImage _converter;

public Convert(ConvertViewModel pConvertViewModel, IConverterImage pConverter)
{
    InitializeComponent();
    _converter = pConverter;
    BindingContext = pConvertViewModel;
    this.Loaded += Convert_Loaded;
    WeakReferenceMessenger.Default.Register(this, (MessageHandler<object, SendMessageToDisplayWaitWindow>)((e, msg) =>
    {
        LoadUnLoadDisplay(msg);
    }));
}

private void LoadUnLoadDisplay(SendMessageToDisplayWaitWindow msg)
{
    try
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var popup = Handler.MauiContext.Services.GetService<WaitPopup>();

            if (msg.OpenOrCloseDisplay)
            {
                this.ShowPopup(popup);
                _converter.ProcessImages(msg.ListImages, msg.FolderSave, msg.FormatSelect);
            }
            else
            {
                popup.Close();
            }
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine(string.Format("**** \n {0}", ex.Message));
    }
}

I don't understand what could be causing this bug, nor what to do to solve it.

Leonardo Silva
  • 341
  • 5
  • 17
  • 3
    why are you doing **everything** on the `MainThread`? – Jason Jun 09 '23 at 16:35
  • *"I don't understand what could be causing this bug, "* UI changes (such as showing popup) cannot occur until any code you are running on MainThread is finished. [Here is example](https://stackoverflow.com/a/76435519/199364) of running some code on a background thread, then UI-related code on MainThread. Start popup inside MainThread block; do potentially long-running logic inside Task.Run block. – ToolmakerSteve Jun 09 '23 at 19:55
  • The suggestions really helped me to solve the problem. I first removed **MainThread** from the code. Then I changed the method **LoadUnLoadDisplay** from void to async Task. Other than that, I put the **_converter.ProcessImages** inside an **await Task.Run(async () =>**, as suggested in the link above. Now the popup appears. Thanks a lot for the help. – Leonardo Silva Jun 09 '23 at 20:33
  • Glad you solved it. You could make an answer below, which may help others with similar issues. – Liqun Shen-MSFT Jun 12 '23 at 07:04

1 Answers1

0

As suggested in the comments, the use of MainThread was what was causing the problem. Removed MainThread from classes 'ConvertViewModel.cs' and 'Convert.xaml.cs'. The LoadUnLoadDisplay method was changed from void to 'async Task'. Other than that, I put the _converter.ProcessImages inside an 'await Task.Run(async() => {});'. Now everything works fine.

ConvertViewModel.cs

public ICommand ConverterCommand => new Command(ConverterImages);

private void ConverterImages(object obj)
{
    try
    {
        if(ListImages.Count == 0)
        {
            App.Current.MainPage.DisplayAlert("Não há imagens para conversão", "Clique no botão '+', selecione a pasta de imagens,\ndepois selecione as imagens desejadas\ne confirme, clicando no botão na parte superior da página.\nDepois siga com a conversão dos arquivos", "Ok");
        }
        else
        {
            var folder = string.Empty;

            switch (FormatSelected)
            {
                case "jpeg":
                    folder = FoldersNames.NamesFolders[0];
                    break;
                case "pdf":
                    folder = FoldersNames.NamesFolders[1];
                    break;
                case "png":
                    folder = FoldersNames.NamesFolders[2];
                    break;
                case "bmp":
                    folder = FoldersNames.NamesFolders[3];
                    break;
                case "tiff":
                    folder = FoldersNames.NamesFolders[4];
                    break;
                case "webp":
                    folder = FoldersNames.NamesFolders[5];
                    break;
                case "gif":
                    folder = FoldersNames.NamesFolders[6];
                    break;
                default:
                    folder = FoldersNames.NamesFolders[2];
                    break;
            }

            string folderSavePath = Path.Combine(FoldersNames.PathRoot, folder);
            var listPaths = (from i in ListImages select i.PathImage).ToList();

            var send = new SendMessageToDisplayWaitWindow()
            {
                OpenOrCloseDisplay = true,
                ListImages = listPaths,
                FolderSave = folderSavePath,
                FormatSelect = FormatSelected
            };
            WeakReferenceMessenger.Default.Send<SendMessageToDisplayWaitWindow>(send);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("***** " + ex.Message);
    }
}

Convert.xaml.cs

private IConverterImage _converter;

public Convert(ConvertViewModel pConvertViewModel, IConverterImage pConverter)
{
    InitializeComponent();
    _converter = pConverter;
    BindingContext = pConvertViewModel;
    this.Loaded += Convert_Loaded;
    WeakReferenceMessenger.Default.Register(this, (MessageHandler<object, SendMessageToDisplayWaitWindow>)((e, msg) =>
    {
        LoadUnLoadDisplay(msg);
    }));
}

private async Task LoadUnLoadDisplay(SendMessageToDisplayWaitWindow msg)
{
    try
    {
        if(ApplicationCache.PopupConvert == null)
            ApplicationCache.PopupConvert = Handler.MauiContext.Services.GetService<WaitPopup>();

        if (msg.OpenOrCloseDisplay)
        {
            this.ShowPopup(ApplicationCache.PopupConvert);

            await Task.Run(async () =>
            {
                _converter.ProcessImages(msg.ListImages, msg.FolderSave, msg.FormatSelect);
            });
        }
        else
        {
            ApplicationCache.PopupConvert.Close();
            ApplicationCache.PopupConvert = null;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(string.Format("**** \n {0}", ex.Message));
        App.Current.MainPage.DisplayAlert(string.Format("Erro ao abrir - {0}", this.Title), "Erro ao abrir uma página do aplicativo", "Ok");
    }
}
Leonardo Silva
  • 341
  • 5
  • 17