我已经尝试谷歌找到我的问题的答案,但没有找到解决方案。
我使用C#和WinForms。我创建了一个面板并为其添加了标签。此面板最初设置为myPanel.Visible = false
。我点击按钮时要设置myPanel.Visible = true
。该按钮正在调用一个函数。在函数调用期间,我想在面板中显示一个progessbar,所以我设置了这个myPanel.Visible = true
,在函数的末尾我将它设置回myPanel.Visible = false
。
问题是,标签不可见。
当我没有在函数末尾设置myPanel.Visible = false
时,标签是可见的,但仅在函数的末尾。
我还尝试以编程方式在所调用的函数中添加标签,但仍无法正常工作。我尝试的第二个想法是在函数调用期间使用this.PerformLayout();
。
似乎应用程序仅在函数调用结束时绘制标签,但我需要在函数调用期间绘制它。
感谢您的帮助。
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;
}
答案 0 :(得分:2)
您的问题显然是您在UI线程中执行任务。因此,当此任务正常工作时,UI本身不会重新绘制。当你完成时,它仍然是隐形的。
请尝试使用BackgroundWorker
。使用其ProgressChanged
事件更新进度条。
如果您展示了一些代码,我可以详细了解实施情况。
<强>更新强>
我实际上更喜欢sstan的答案,但我答应以BackgroundWorker
方式展示。如果由于某种原因您无法使用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;
}
}
如您所见,BackgroundWorker
相当容易使用,但async/await
更加容易,因为您不必为并行化编写如此多的代码。
答案 1 :(得分:1)
您看到的行为是正常的。只有当UI线程有机会重新绘制窗口时,可见性的更改才会生效。但是,当您的函数正在运行时,UI线程正忙,因此它无法重新绘制您的窗口。 UI线程仅在函数调用结束时释放,因此当组件的可见性更改生效时。
您需要做的是在不同的线程上执行函数的工作,以便UI函数在函数仍在运行时可以自由重绘窗口。
执行此操作的一种方法是将Task.Run()与async/await关键字结合使用。以下是使用您发布的代码的基本示例:
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...
}
正如Rene在评论中指出的那样,请记住,您只能在UI线程上进行UI工作。因此,在上面的示例中,您将注意到进度报告是通过Progress<T>类执行的,该类允许您从单独的线程更改进度条值(UI工作),因为它负责确保进度报告发生在UI线程上。