Cristiano Maia
Cristiano Maia

Reputation: 2042

When does the BeginInvoke method runs?

I have two threads which uses the BeginInvoke method to change some Windows Form object's (Panel and Label) visibility attribute to false.The problem is that I'm not sure when the change happens. I can see that the panel is not there (so the BeginInvoke method works) but my if condition to check the visibility status always returns true the first time the form is activated.

bool notVisible = false;

private void LunchMainScreen_Activated(object sender, EventArgs e) {
    String CurrentSite = "";
    List<DateTime> availableDates = new List<DateTime>();

    // Get available dates
    Thread availableDatesThread = new Thread(delegate() {
        availableDates = LunchUserPreferences.GetUserAvailableDates();
            changeObjVisible(notVisible, selectAvailabilityPanel);
            changeObjVisible(notVisible, whenLbl);
        }
    });
    availableDatesThread.Start();

    // Get user current site
    Thread checkSiteThread = new Thread(delegate() {
        CurrentSite = LunchUserPreferences.GetUserSite();
            changeObjVisible(notVisible, selectSitePanel);
            changeObjVisible(notVisible, whereLbl);
        }
        updateText(CurrentSite, CurrentSiteSetLbl);
    });
    checkSiteThread.Start();

    while (selectSitePanel.Visible == false && selectAvailabilityPanel.Visible == false) {
         // it NEVER gets here, even though the panels are NOT visible when the program loads
         WhoLunchTable.Visible = false;
         WhoLunchTable.SuspendLayout();

         listOfAvailableGroups.Clear();
         WhoLunchTable.Controls.Clear();
         WhoLunchTable.RowStyles.Clear();

         PopulateTable();

         WhoLunchTable.Visible = true;
         WhoLunchTable.ResumeLayout();
         break;
    }
}

private delegate void changeObjVisibleDelegate(bool visibility, object obj);

private void changeObjVisible(bool visibility, object obj) {
    if (this.InvokeRequired) {
        this.BeginInvoke(new changeObjVisibleDelegate(changeObjVisible), new object[] { visibility, obj });
        return;
    }

    // downcast to the correct obj 
    if (obj is Panel) {
        Panel panel = (Panel)obj;
        panel.Visible = visibility;
    }

    if (obj is Label) {
        Label lbl = (Label)obj;
        lbl.Visible = visibility;
    }
}

private delegate void updateTextDelegate(string text, Label lbl);
private void updateText(string text, Label lbl) {
    if (this.InvokeRequired) {
        this.BeginInvoke(new updateTextDelegate(updateText), new object[] { text, lbl });
        return;
    }

    lbl.Text = text;
}

It does work fine when the Form is activated for the second time, for example:

  1. The form loads for the first time and it doesn't go inside the while loop.
  2. I minimize the form/program.
  3. The LunchMainScreen_Activated runs again and it works as it should because it recognises that the panels are not visible.

UPDATE:

I had an idea after reading AlexF answer which solved the problem but it doesn't look like the ideal solution:

I've created a while condition that will only stop when both threads are not alive and an if condition inside it that will get this point in time and execute what I need:

while (availableDatesThread.IsAlive || checkSiteThread.IsAlive) {
    // At least one thread is still alive, keeps checking it...

    if (!availableDatesThread.IsAlive && !checkSiteThread.IsAlive) {
        // Both threads should be dead now and the panels not visible

         WhoLunchTable.Visible = false;
         WhoLunchTable.SuspendLayout();

         listOfAvailableGroups.Clear();
         WhoLunchTable.Controls.Clear();
         WhoLunchTable.RowStyles.Clear();

         PopulateTable();

         WhoLunchTable.Visible = true;
         WhoLunchTable.ResumeLayout();
         break;
    }
}

Upvotes: 1

Views: 324

Answers (1)

AlexF
AlexF

Reputation: 497

Reading your code the first time the code doesn't enter in the while loop because selectSitePanel.Visible and selectAvailabilityPanel.Visible are true: this is because the availableDatesThread.Start(); and checkSiteThread.Start(); are started but not finished; those two calls are not blocking so the code continues and skips the while. Meanwhile the two backgrund threads finishes so the second time the "Activated" event is raised the variables values are "correct" (at least for the last cycle).

Without waiting for the threads to finish you are rushing through the code before having a result for the needed value.

In other words, it's better to not use a background thread to update an interface for the use you need.

If you need you may continue to use the code the way you are using it but moving the "while" section in two separate functions: they may be called when the threads have finished their work and refresh the window in this moment and not in the "activate" event.

Upvotes: 1

Related Questions