-1

Multi-threading is a concept I am attempting to understand. I Am simply trying to create a "Working Please Wait" window with a Text message update as to the progress.

I cannot seem to Send a Message back to that thread to update the text message.

Here is how I am Starting my Thread:

frmProcessing Fp;

Thread FpThread = new Thread(() => {
    Fp= new frmProcessing();
    Fp.Tital("Building Database");
    Fp.Working("Clearing Old Data...");
    Fp.ShowDialog();
});

FpThread.SetApartmentState(ApartmentState.STA);
FpThread.Start();   

Attempting to run this function from the Thread preforming the updating action(original thread):

Fp.Working("new message");

I attempted this:

Fp.Invoke(new Action(() => {
    Fp.Working("new message");
}));

I get this error:

'Fp' is not null here.
CS0165:Use of unassigned local variable 'fp'

Jimi
  • 29,621
  • 8
  • 43
  • 61
zapdbf
  • 107
  • 6
  • Does this help answer your question? [Background worker is not reporting progress Winforms](https://stackoverflow.com/questions/72568511/background-worker-is-not-reporting-progress-winforms/72580963#72580963) – IVSoftware Jun 12 '22 at 15:15
  • Not directly. I believe my problem has to do with access to the object. Possibly the invoke method of the form is not the way to access the thread message loop. I am not sure I even understand the problem correctly. – zapdbf Jun 12 '22 at 15:24
  • I am just trying to run a couple of functions and close the wait window properly in another thread. It does not seem like it should be this difficult. – zapdbf Jun 12 '22 at 15:26
  • In the `Fp.Tital([some text])` method (which I assume sets the Text of the Form or some other Control), write just, e.g., `BeginInvoke(new Action(() => Text = [some text]));`, so it doesn't matter if you call that method from the same Thread or another Thread. -- Do you have a specific reason to start a Thread that shows a Form? Or is it just for the fun of it? – Jimi Jun 12 '22 at 15:32
  • its Underlining Fp in red. and the message I get is confusing. It says Fp is not null but then it also says it is unassigned. if I try Fp.BeginInvoke(new Action(() => Text = "Test")); i still get a red underlined Fp. – zapdbf Jun 12 '22 at 15:43
  • You have `nullable` enabled, just declare your Form object as `private frmProcessing? fp = null;` -- Who said to call `Fp.BeginInvoke(...)`? I suggested to add that code to the `Tital()` and / or `Working()` methods of `frmProcessing`. – Jimi Jun 12 '22 at 15:51
  • OK, that was the part I was confused about the BeginInvoke should have been used in the Form on the other thread. On Everything I read it did not seem clear to me. Thanks. – zapdbf Jun 12 '22 at 16:04
  • Here's what I don't understand about your question. Ordinarily, we have the visible UI thread and we want it to stay responsive to clicks and keys. For that reason, long-running tasks for things like updating databases are relegated to a worker thread. And as that thread runs, it sends messages back to the UI thread to say what the progress is. But you say that you're performing the major work of the update task on the "original thread". That seems backwards to me, but am I understanding you correctly? – IVSoftware Jun 12 '22 at 16:06
  • BTW, prefer this form: `using (Fp = new frmProcessing()) { // [...] fp.ShowDialog(); }`. This disposes the modal Form when it's closed. -- You must dispose of modal Forms implicitly or explicitly, because simply closing a modal Form cannot dispose of it. – Jimi Jun 12 '22 at 16:11
  • This task was to recreate a local sql database based on stored procedures in a cloud sql server. , Basically Resetting the application. Therefore no other tasks should be ran. But my "Wait Window" had animation and progress updates for the user. – zapdbf Jun 12 '22 at 16:13
  • Thanks, got it. Now I understand the `modal` bit. The UI thread is deliberately locked out, yes? – IVSoftware Jun 12 '22 at 16:15
  • I call the Dispose method of the form. – zapdbf Jun 12 '22 at 16:15
  • yes that is correct. – zapdbf Jun 12 '22 at 16:16
  • Thanks everyone for their help, I understand it a little better now. Still allot to learn about multi-threading. – zapdbf Jun 12 '22 at 16:18
  • *The UI thread is deliberately locked out*: I don't know what that means. You have two Threads that run a Message Loop. The original UI Thread and a new Thread, where the Message Loop is run when you call `ShowDialog()` (otherwise, you should call `Application.Run()`) -- You Invoke (in a way or another), in the method that *is* or *could* be called from another Thread. I suggested to use `BeginInvoke()`, since it can be safely called from the same Thread. To use `Invoke()`, you need to check `InvokeRequired` (so you don't invoke in the same Thread - remember it's synchronous). – Jimi Jun 12 '22 at 16:21

2 Answers2

0

Based on our conversation, here's one of many ways achieve that outcome. (In other words, this is how I personally do this kind of thing.)

public partial class MainForm : Form
{
    private void buttonModal_Click(object sender, EventArgs e)
    {
        var modal = new Progress();

        // Start a worker task to perform the
        // SQL updates and reset the application.
        Task.Run(() => 
        {
            for (int i = 0; i < 100; i++)
            {
                // Block on the thread doing the updating.
                // What you do here is up to you. This just
                // simulates work that takes some amount of time.
                Task.Delay(25).Wait();

                modal.SetProgress(i);
            }
            Invoke((MethodInvoker)delegate{ modal.Dispose(); });
        });

        // The modal dialog will prevent user interaction,
        // effectively locking user out until the update completes.
        modal.ShowDialog();
    }
}

Progress dialog with no title bar

where...

public partial class Progress : Form
{
    public void SetProgress(int i)
    {
        if (IsHandleCreated)    // Avoid potential race condition
        {
            Invoke((MethodInvoker)delegate { progressBar1.Value = i; });
        }
    }

    // This is probably in Progress.Designer.cs file
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // A debug message to confirm that this dialog is getting disposed.
            Debug.WriteLine("Progress dialog is now Disposing");
            if (components != null)
            {
                components.Dispose();
            }
        }
        base.Dispose(disposing);
    }
}
IVSoftware
  • 5,732
  • 2
  • 12
  • 23
0

Anyone Finding this Post. What fixed my problem was 3 parts. setting the NewForm to Null.

frmProcessing Fp;

now

frmProcessing Fp=null;

using BeginInvoke() functions on the Form running in the separate thread. Not from the function trying to access the form.

public void Working(string Working) // function on form running in new thread
        {

          BeginInvoke(new Action(() =>  lblProcess.Text = Working));
        
        }

More Code can be added to test to see if the message is coming from another thread, to be more complete.

Access to the methods and functions of the form in the new thread takes some time to start, so in my case I delayed accessing the forms methods for about 1 second by

threading.sleep(1000);

There might be Better ways to accomplish this task. But it works for me.

zapdbf
  • 107
  • 6
  • Thanks, I believe it's useful to know how you resolved your issue and getting things to "work for _you_" is what it's all about. I posted an answer to make sure the question would _have_ one, but not to take away from yours in the least. I remain concerned that you're spawning a new STA thread, especially since you say that you're in the learning curve of multi-threading. The last thing I want to say about this is please read this excellent SO answer [How will STAThread affect my multi-threaded program](https://stackoverflow.com/a/24337068/5438626) by the venerable Hans Passant. – IVSoftware Jun 12 '22 at 20:19