Search code examples
c#multithreadingbackground-processwinui-3winui

How to implement background polling in WinUI application in c#


I have a WinUI application where I can select the serial port I need for my tasks, from which data will be read in the future. The problem is that I need to do the polling in the background, regardless of whether the interface is open or not. The polling is carried out in a cycle until it is stopped via the tray. That is, when the application is launched, or when a button is pressed, a background process should be launched in which the polling will take place, and which will continue the polling even when the application window is closed. As far as I know, PowerToys is also made on WinUI, and there, pressing key combinations to open the interface is somehow processed, I wonder how this is implemented

I tried: 1) starting threads, but they stop when the window is closed, and the polling stopped. 2) starting the windows service to do the polling in it, but the service refuses to start, just when calling ServiceBase.Run() nothing happens (maybe I did something wrong).

Maybe there is a way to hide the window without ending the program process, and open it back when necessary? or are there any other ways to solve the problem. I will be glad to any advice


Solution

  • To prevent the Window from closing, you can use Closing event:

    App.xaml.cs

    private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
    {
        args.Cancel = true;
        sender.Hide();
    }
    

    And to do the polling, you can use the PeriodicTimer:

    App.xaml.cs

    private async Task StartBackgroundTask(CancellationToken cancellationToken = default)
    {
        using var timer = new PeriodicTimer(period: TimeSpan.FromSeconds(3));
    
        try
        {
            // ConfigureAwait(false) will release the UI thread and allow the background task to run on a different thread.
            while (await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false) is true)
            {
                // Do your logic for the serial port here.
    
                // Since this thread won't be the UI thread, you need to use the DispatcherQueue to access/update UI elements.
                _window?.DispatcherQueue.TryEnqueue(() =>
                {
                    if (_window.AppWindow.IsVisible is false)
                    {
                        _window.AppWindow.Show();
                    }
                });
            }
        }
        catch (OperationCanceledException)
        {
            System.Diagnostics.Debug.WriteLine("Background task was cancelled.");
        }
        catch (Exception exception)
        {
            System.Diagnostics.Debug.WriteLine(exception);
        }
    }
    
    private CancellationTokenSource CancellationTokenSource { get; } = new();
    
    protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        _window = new MainWindow();
        _window.AppWindow.Closing += AppWindow_Closing;
        _window.Activate();
        await StartBackgroundTask(CancellationTokenSource.Token);
    }