In my program [C# + winforms]. I have progress bar & listview.
在我的程序[C#+ winforms]中。我有进度条和列表视图。
Through one method i am performing some operations & then updating data in Listview. The no of records added is the value i am setting for ProgressBar.value property. What i want here is, According to value of progress bar, it should show its progress. However the progress bar is not getting updated. Only at the end of method execution progress bar shows entire progress i.e. 100 %
通过一种方法,我正在执行一些操作,然后更新Listview中的数据。添加的记录数是我为ProgressBar.value属性设置的值。我想要的是,根据进度条的值,它应该显示其进度。但是,进度条未更新。仅在方法执行结束时,进度条显示整个进度,即100%
Can someone help me in this regard?
有人可以在这方面帮助我吗?
Thanks, Amit
4 个解决方案
#1
It sounds like you are blocking the UI thread - i.e. you haven't released the system to do any painting.
听起来你正在阻止UI线程 - 即你没有释放系统来做任何绘画。
A hacky answer is to inject Application.DoEvents()
into your code - but this is risky, and has problems with re-entrancy etc; and it is just a bit hacky.
一个hacky的答案是将Application.DoEvents()注入到您的代码中 - 但这是有风险的,并且存在重新入侵等问题;它只是有点hacky。
A better option may be to do the processing on a BackgroundWorker
, periodically switching to the UI thread to update things (Control.Invoke) - but this may be tricky if you are adding lots of items to a ListView
.
更好的选择可能是在BackgroundWorker上进行处理,定期切换到UI线程来更新内容(Control.Invoke) - 但如果要向ListView添加大量项目,这可能会很棘手。
Full example (although you might want to batch the UI updates - not a row at a time):
完整示例(尽管您可能希望批量处理UI更新 - 但不是一次一行):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class MyForm : Form
{
BackgroundWorker worker;
ListView list;
Button btn;
ProgressBar bar;
public MyForm()
{
Text = "Loader";
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
list = new ListView();
list.Dock = DockStyle.Fill;
Controls.Add(list);
btn = new Button();
btn.Text = "Load";
btn.Dock = DockStyle.Bottom;
Controls.Add(btn);
btn.Click += btn_Click;
bar = new ProgressBar();
bar.Dock = DockStyle.Top;
Controls.Add(bar);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn.Enabled = true;
}
void btn_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
btn.Enabled = false;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
string newRow = "Row " + i.ToString();
worker.ReportProgress(i, newRow);
Thread.Sleep(100);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
list.Items.Add((string)e.UserState);
bar.Value = e.ProgressPercentage;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
}
#2
Really Sorry Friends,
真的很抱歉朋友,
Actually, I was assiging value to ProgressBar.value field but didnt use update() method. I used that & my problem got resolved.
实际上,我正在为ProgressBar.value字段赋值,但没有使用update()方法。我用过那个,我的问题得到了解决。
Thanks all for your replies
谢谢大家的回复
#3
As Marc said, you want to make sure that you spin off a new thread to do your long running computation. That way the User Interface thread (which is the one that has to do all the screen updates) can redraw the progres bar whenever you change the percent complete.
正如Marc所说,你想确保你创建一个新的线程来进行长时间运行的计算。这样,每当您更改完成百分比时,用户界面线程(必须执行所有屏幕更新的线程)都可以重绘进度条。
It's important to note that only the UI thread can update the interface. So, once you are running on a separate thread, you have to go through an extra hoop to make sure that your UI change is processed on the UI thread. If you aren't sure what thread you are running on, you can check the value of InvokeRequired (if your class is a System.Windows.Form) to see if you are actualy in the UI thread.
重要的是要注意只有UI线程才能更新界面。因此,一旦你在一个单独的线程上运行,你必须经过一个额外的箍,以确保在UI线程上处理你的UI更改。如果您不确定正在运行什么线程,可以检查InvokeRequired的值(如果您的类是System.Windows.Form),以查看您是否在UI线程中是实际的。
To get your command processed on the UI thread, use the Control.Invoke() function to make sure the update is processed on the UI thread for the control you are working with.
要在UI线程上处理命令,请使用Control.Invoke()函数确保在您正在使用的控件的UI线程上处理更新。
In my sample code below I'm creating a delegate function type and declaring the invoked function in advance....I've not done it with any of the cool C# 3.5 functions, but I bet you could work up a lamba expression to do the same thing.
在下面的示例代码中,我正在创建一个委托函数类型并提前声明调用的函数....我没有使用任何很酷的C#3.5函数,但我打赌你可以编写一个lamba表达式做同样的事。
private void bCreateInvoices_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(CreateInvoices);
worker.RunWorkerAsync(this);
}
// Here is the long running function that needs to update the progress bar
public void CreateInvoices(object sernder, DoWorkEventArgs e)
{
int totalChecked = CountCheckedServiceOrders();
int totalCompleted = 0;
foreach (...data to process...) {
totalCompleted++;
if (InvokeRequired) {
Invoke(new Change(OnChange), "status text",
totalCompleted, totalChecked);
}
}
}
// this code updates the status while a background thread works
private delegate void Change(string status, int complete, int total);
private void OnChange(string status, int complete, int total)
{
if (status == null) {
progressBar.Visible = false;
lStatus.Text = "Task complete";
progressBar.Value = 0;
} else {
progressBar.Visible = true;
progressBar.Minimum = 0;
progressBar.Maximum = total;
progressBar.Value = complete;
lStatus.Text = status;
}
}
Take a look at the MSDN Control.InvokeRequired manual page and the MSDN Control.Invoke manual page for some more info.
请查看MSDN Control.InvokeRequired手册页和MSDN Control.Invoke手册页以获取更多信息。
#4
The ProgressBar.Value must be between 0 and 100.
ProgressBar.Value必须介于0到100之间。
My guess is that your problem is that you're updating the ListView on the GUI thread. That means you'll need to call Application.DoEvents()
after changing the ProgressBar.Value
property.
我的猜测是你的问题是你正在更新GUI线程上的ListView。这意味着您需要在更改ProgressBar.Value属性后调用Application.DoEvents()。
It would be best to run on a BackgroundWorker and use the ProgressChanged
event to handle the ProgressBar
update.
最好在BackgroundWorker上运行并使用ProgressChanged事件来处理ProgressBar更新。
Here's another question about the same topic.
这是关于同一主题的另一个问题。
#1
It sounds like you are blocking the UI thread - i.e. you haven't released the system to do any painting.
听起来你正在阻止UI线程 - 即你没有释放系统来做任何绘画。
A hacky answer is to inject Application.DoEvents()
into your code - but this is risky, and has problems with re-entrancy etc; and it is just a bit hacky.
一个hacky的答案是将Application.DoEvents()注入到您的代码中 - 但这是有风险的,并且存在重新入侵等问题;它只是有点hacky。
A better option may be to do the processing on a BackgroundWorker
, periodically switching to the UI thread to update things (Control.Invoke) - but this may be tricky if you are adding lots of items to a ListView
.
更好的选择可能是在BackgroundWorker上进行处理,定期切换到UI线程来更新内容(Control.Invoke) - 但如果要向ListView添加大量项目,这可能会很棘手。
Full example (although you might want to batch the UI updates - not a row at a time):
完整示例(尽管您可能希望批量处理UI更新 - 但不是一次一行):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class MyForm : Form
{
BackgroundWorker worker;
ListView list;
Button btn;
ProgressBar bar;
public MyForm()
{
Text = "Loader";
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
list = new ListView();
list.Dock = DockStyle.Fill;
Controls.Add(list);
btn = new Button();
btn.Text = "Load";
btn.Dock = DockStyle.Bottom;
Controls.Add(btn);
btn.Click += btn_Click;
bar = new ProgressBar();
bar.Dock = DockStyle.Top;
Controls.Add(bar);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn.Enabled = true;
}
void btn_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
btn.Enabled = false;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
string newRow = "Row " + i.ToString();
worker.ReportProgress(i, newRow);
Thread.Sleep(100);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
list.Items.Add((string)e.UserState);
bar.Value = e.ProgressPercentage;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
}
#2
Really Sorry Friends,
真的很抱歉朋友,
Actually, I was assiging value to ProgressBar.value field but didnt use update() method. I used that & my problem got resolved.
实际上,我正在为ProgressBar.value字段赋值,但没有使用update()方法。我用过那个,我的问题得到了解决。
Thanks all for your replies
谢谢大家的回复
#3
As Marc said, you want to make sure that you spin off a new thread to do your long running computation. That way the User Interface thread (which is the one that has to do all the screen updates) can redraw the progres bar whenever you change the percent complete.
正如Marc所说,你想确保你创建一个新的线程来进行长时间运行的计算。这样,每当您更改完成百分比时,用户界面线程(必须执行所有屏幕更新的线程)都可以重绘进度条。
It's important to note that only the UI thread can update the interface. So, once you are running on a separate thread, you have to go through an extra hoop to make sure that your UI change is processed on the UI thread. If you aren't sure what thread you are running on, you can check the value of InvokeRequired (if your class is a System.Windows.Form) to see if you are actualy in the UI thread.
重要的是要注意只有UI线程才能更新界面。因此,一旦你在一个单独的线程上运行,你必须经过一个额外的箍,以确保在UI线程上处理你的UI更改。如果您不确定正在运行什么线程,可以检查InvokeRequired的值(如果您的类是System.Windows.Form),以查看您是否在UI线程中是实际的。
To get your command processed on the UI thread, use the Control.Invoke() function to make sure the update is processed on the UI thread for the control you are working with.
要在UI线程上处理命令,请使用Control.Invoke()函数确保在您正在使用的控件的UI线程上处理更新。
In my sample code below I'm creating a delegate function type and declaring the invoked function in advance....I've not done it with any of the cool C# 3.5 functions, but I bet you could work up a lamba expression to do the same thing.
在下面的示例代码中,我正在创建一个委托函数类型并提前声明调用的函数....我没有使用任何很酷的C#3.5函数,但我打赌你可以编写一个lamba表达式做同样的事。
private void bCreateInvoices_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(CreateInvoices);
worker.RunWorkerAsync(this);
}
// Here is the long running function that needs to update the progress bar
public void CreateInvoices(object sernder, DoWorkEventArgs e)
{
int totalChecked = CountCheckedServiceOrders();
int totalCompleted = 0;
foreach (...data to process...) {
totalCompleted++;
if (InvokeRequired) {
Invoke(new Change(OnChange), "status text",
totalCompleted, totalChecked);
}
}
}
// this code updates the status while a background thread works
private delegate void Change(string status, int complete, int total);
private void OnChange(string status, int complete, int total)
{
if (status == null) {
progressBar.Visible = false;
lStatus.Text = "Task complete";
progressBar.Value = 0;
} else {
progressBar.Visible = true;
progressBar.Minimum = 0;
progressBar.Maximum = total;
progressBar.Value = complete;
lStatus.Text = status;
}
}
Take a look at the MSDN Control.InvokeRequired manual page and the MSDN Control.Invoke manual page for some more info.
请查看MSDN Control.InvokeRequired手册页和MSDN Control.Invoke手册页以获取更多信息。
#4
The ProgressBar.Value must be between 0 and 100.
ProgressBar.Value必须介于0到100之间。
My guess is that your problem is that you're updating the ListView on the GUI thread. That means you'll need to call Application.DoEvents()
after changing the ProgressBar.Value
property.
我的猜测是你的问题是你正在更新GUI线程上的ListView。这意味着您需要在更改ProgressBar.Value属性后调用Application.DoEvents()。
It would be best to run on a BackgroundWorker and use the ProgressChanged
event to handle the ProgressBar
update.
最好在BackgroundWorker上运行并使用ProgressChanged事件来处理ProgressBar更新。
Here's another question about the same topic.
这是关于同一主题的另一个问题。