MksN
MksN

Reputation: 3

Label not visible on Panel when calling visible = true

I already tried google to find an answer to my problem but haven't found a solution.

I working with C# and WinForms. I created a panel and added a label to it. This panel is set to myPanel.Visible = falseat first. I want to set it myPanel.Visible = true when I click a button. The button is calling a function. During the function call I want to show a progessbar in the panel, so I set this myPanel.Visible = trueand at the end of the function I set it back to myPanel.Visible = false.

The problem is, that the label isn't visible.

When I don't set myPanel.Visible = false at the end of the function, the label is visible, but only at the end of the function.

I also tried to programmatically add the label in the function called, still not working. The second idea I tried was to use this.PerformLayout(); during the call of the function.

It seems like that the application is drawing the label only at the end of the function call, but I need it to be drawn during the function is called.

Thanks for any help.

        private void buttonAdd_Click(object sender, EventArgs e)
        {
            //Adding label to panel
            MyLabel label = new MyLabel();
            label.Text = "Test";
            label.Location = new Point(0, 0);

            progressPanel.Controls.Add(label);

            //Showing progressPanel
            progressPanel.Visible = true;

            progressBar1.Minimum = 1;
            progressBar1.Value = 1;
            progressBar1.Step = 1;

            //Some Code

            progressPanel.Visible = false;
        }

Upvotes: 0

Views: 4042

Answers (2)

René Vogt
René Vogt

Reputation: 43916

Your problem is obviously that you are performing your task in the UI thread. So the UI itself doesn't get repainted while this task is working. And when you finished, it's still invisible.

Try using a BackgroundWorker instead. Use its ProgressChanged event to update your progress bar.

If you show some code, I can go into details of implementation.

UPDATE:

I actually prefer sstan's answer, but I promised to show the BackgroundWorker way. It may still be helpful if for some reason you cannot use async/await:

public partial class Form1 : Form
{
    private BackgroundWorker _backgroundWorker;
    public Form1()
    {
        InitializeComponent();
        _backgroundWorker.DoWork += DoWork;
        _backgroundWorker.RunWorkerCompleted += WorkerCompleted;
        _backgroundWorker.WorkerReportsProgress = true;
        _backgroundWorker.ProgressChanged += WorkerProgressed;
    }

    private void buttonAdd_Click(object sender, EventArgs e)
    {
        //Adding label to panel
        MyLabel label = new MyLabel();
        label.Text = "Test";
        label.Location = new Point(0, 0);

        progressPanel.Controls.Add(label);

        //Showing progressPanel
        progressPanel.Visible = true;

        progressBar1.Minimum = 0;
        progressBar1.Maximum = 100;
        progressBar1.Value = 0;
        progressBar1.Step = 1;

        // to avoid multiple starts
        buttonAdd.Enabled = false;

        // start working
        _backgroundWorker.RunWorkerAsync();        
    }
    private void DoWork(object sender, DoWorkEventArgs e)
    {
        _backgroundWorker.ReportProgress(1);

        // work

        _backgroundWorker.ReportProgress(50);

        // more work

        _backgroundWorker.ReportProgress(100);
    }
    private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progressPanel.Visible = false;
        buttonAdd.Enabled = true;
    }
    private void WorkerProgress(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }
}

So as you see, BackgroundWorkers are fairly easy to use, but async/await is even easier since you don't have to write so much code just for parallelization.

Upvotes: 2

sstan
sstan

Reputation: 36523

The behavior you see is normal. The changes in visibility only take effect when the UI thread gets a chance to repaint your window. But, while you function is running, the UI thread is busy, so it can't repaint your window. The UI thread only frees up at the end of your function call, so that's when the component's visibility changes take effect.

What you need to do is to perform your function's work on a different thread so that the UI thread is free to repaint your window while the function is still running.

One way to do this, is by using Task.Run() combined with the async/await keywords. Here is a basic example of how this could look like, using the code you posted:

async private void buttonAdd_Click(object sender, EventArgs e)
{
    // ....

    //Showing progressPanel
    progressPanel.Visible = true;

    progressBar1.Minimum = 1;
    progressBar1.Value = 1;
    progressBar1.Step = 1;

    // this work will happen on separate thread,
    // so the UI thread will be free to update the panel visibility
    Progress<int> progress = new Progress<int>(percentage => progressBar1.Value = percentage);
    await Task.Run(() => this.WorkToBePerformedOnSeparateThread(progress));

    progressPanel.Visible = false;
}

private void WorkToBePerformedOnSeparateThread(IProgress<int> progress)
{
    // do work...
    progress.Report(25); // Report 25% completed...

    // do more work

    progress.Report(50); // Report 50% completed...

    // more work

    progress.Report(75); // Report 75% completed...

    // etc...
}

As pointed out by Rene in the comments, just remember that you can only do UI work on the UI thread. So, in the example above, you'll notice that the progress reporting is performed through the Progress<T> class which allows you to change the progress bar value (UI work) from the separate thread, because it takes care of ensuring that the progress reporting happens on the UI thread.

Upvotes: 1

Related Questions