0

I have an application which has a C# front end (GUI client), and a C++ back end (business logic). The back end is responsible for requesting progress bar functionality and does this by issuing events which the front-end has observer delegates to respond to and act upon. so events are dispatched and responded to in the main thread.

I would like to spawn a second thread to display and update the progress bar, as the main thread is occupied by the back-end doing its thing. While I can show the progress bar window when supposed to, and correctly hide when finished, it will not respond to updates/increments.

ProgressBarWindow holds the progress bar control (pBar). ProgressBarWindow is owned by progressBarThread.

public static EventObserver.ProgressBeginEvent pbe = null;
public static EventObserver.ProgressFinishEvent pfe = null;
public static EventObserver.ProgressIncrementEvent pie = null;
public static Thread progressBarThread = null;

static private void InitialiseProgressBarManager()
{

    // Setup the progress bar callbacks...
    pbe = new EventObserver.ProgressBeginEvent(delegate
        (int currentIncrements, int totalIncrements, string message)
    {
        // Create the thread and progress bar window...
        progressBarThread = new Thread(() =>
        {
            ProgressBarWindow sw = new ProgressBarWindow();
            sw.pBar.IsIndeterminate = false;
            sw.pBar.Minimum = currentIncrements.Value;
            sw.pBar.Maximum = totalIncrements.Value;
            sw.pBar.Value = 0;
            sw.Show();

            pie = new EventObserver.ProgressIncrementEvent(delegate ()
            {
                sw.pBar.Value++;        // The calling thread cannot access this object... see below 
            });

            pfe = new EventObserver.ProgressFinishEvent(delegate ()
            {
                progressBarThread.Abort();
                progressBarThread = null;
            });
        });

        progressBarThread.SetApartmentState(ApartmentState.STA);
        progressBarThread.IsBackground = true;
        progressBarThread.Start();
    });
}

The window shows when expected, and the ProgressIncrementEvent gets raised correctly (it is owned by the thread), however I get an exception when it tries to access the value.

The calling thread cannot access this object because a different thread owns it.

Do I need a mutex or some other lock? I was hoping the scope of the observer delegate would allow the delegate to have mutable access to the thread local ProgressBarWindow even though it is being invoked from the main thread?

I'm not a great C# developer and even less so C# threading so I am a bit stuck about what I should be doing here or even if I can achieve this behaviour. Any help or direction to get this working would be appreciated.

P.S I am using WPF with ProgressBarWindow being defined in XAML.

mm8
  • 163,881
  • 10
  • 57
  • 88
lfgtm
  • 1,037
  • 6
  • 19
  • The main thread in a GUI app is the UI thread. You should not do hard work on the UI thread. Instead, spawn a worker thread and do hard work there. Let your main (UI) thread only do GUI updates and launch another worker threads. – dymanoid Jul 10 '20 at 11:46
  • @MickyD It is very similar and I did see it, however it didn't mention about the child thread having the GUI control, and I think that might be my problem. Might need a slight redesign as unsure how the frontend can decided if to perform the operation in the background or not. Many thanks. – lfgtm Jul 10 '20 at 12:21

1 Answers1

0

Try to access the control using the window´s dispatcher:

sw.Dispatcher.BeginInvoke(new Action(() => sw.pBar.Value++));
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Is there another official `ProgressBar` with an `IsIndeterminate` property? – mm8 Jul 10 '20 at 12:09
  • @MickyD Sorry for not mentioning it, I am using WPF, and yes `ProgressBarWindow` has the `ProgressBar` member called `pBar`. Have updated. – lfgtm Jul 10 '20 at 12:18
  • 1
    @MickyD: No. `pBar` is the `ProgressBar` in the window... – mm8 Jul 10 '20 at 12:18
  • This answer I think is correct. It's not working for me yet, however I was wondering how to Invoke the control and this answers that bit for me. Thanks. I won't mark it correct until I get it working. – lfgtm Jul 10 '20 at 12:19
  • 1
    _"pBar is the ProgressBar in the window"_ - Ohhhhh my bad. Another gin and tonic for Micky. +1 –  Jul 10 '20 at 12:21
  • 1
    @MickyD agree, the way I feel atm I could do with a fair few of those right now... ;) – lfgtm Jul 10 '20 at 12:22
  • As well as this answer, I had to add a begin invoke action to the thread, `System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => { ... }));` with my ProgressBarWindow initialisation stuff and then all worked as intended. Thank you. – lfgtm Jul 10 '20 at 15:17