如何在进度条完成时触发事件
我创建了一个Windows窗体程序,用于分解文件并将其组件发送到服务器。这些文件很大,所以我创建了一个progressBar,因此用户不会在事务发生时冻结它。我想要做的是有一些机制,只有当所有线程完成而不会阻塞UI线程时才会主动触发(再次,因此平台不会认为它被冻结)。我能想到的最好的方式是被动地“等到真实”,但我觉得必须有更好的方式来做到这一点。我尝试过尝试创建一个事件或回调,但老实说,我刚刚结束时比我开始时更困惑。下面是我现在怎么做这样一个例子:如何在进度条完成时触发事件
public partial class Program : Form
{
private readonly OpenFileDialog _ofd = new OpenFileDialog();
public delegate void BarDelegate();
private string _path;
private void button1_Click(object sender, EventArgs e)
{
if (_ofd.ShowDialog() != DialogResult.OK) return;
textBox1.Text = _ofd.SafeFileName;
_path = _ofd.FileName;
}
private void button2_Click(object sender, EventArgs e)
{
var allLinesFromFile = File.ReadAllLines(_path);
progressBar1.Minimum = 0;
progressBar1.Maximum = allLinesFromFile.Length;
Task.Factory.StartNew(() => Parallel.ForEach(allLinesFromFile, DoSomething));
while (progressBar1.Value < progressBar1.Maximum) //there has to be a better way to do this...
{
MessageBox.Show("Please wait.", "Record Poster", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
//some processes here which should only take place after all threads are complete.
var postingComplete = MessageBox.Show("The posting is complete!", "Record Poster", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
if (postingComplete == DialogResult.OK) Environment.Exit(0);
}
private void DoSomething(string record)
{
//some string manipulation and server transactions here
BeginInvoke(new BarDelegate(() => progressBar1.Increment(1)));
}
}
为尝试使用微软的Reactive Framework(NuGet“System.Reactive.Windows.Forms”)。那么你的代码变成:
private void button2_Click(object sender, EventArgs e)
{
var allLinesFromFile = File.ReadAllLines(_path);
progressBar1.Minimum = 0;
progressBar1.Maximum = allLinesFromFile.Length;
IDisposable subscription =
allLinesFromFile
.ToObservable()
.SelectMany(f => Observable.Start(() => DoSomething(f)))
.ObserveOn(this)
.Do(x => progressBar1.Value += 1)
.Subscribe(x => { },() =>
{
var postingComplete = MessageBox.Show("The posting is complete!", "Record Poster", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
if (postingComplete == DialogResult.OK)
{
Application.Exit();
}
});
}
private void DoSomething(string record)
{
System.Threading.Thread.Sleep(5);
}
如果您需要停止这种早期然后就打电话subscription.Dispose()
。我已经测试过了,它对我来说工作正常。
我很欣赏答案,但是这似乎是在所有线程完成之前处理订阅操作。有些计算应该只在所有线程完成后才会发生,并且此解决方案将允许订阅模块在最后一个线程被触发时但在它实际发布到服务器之前运行。你可以这样说,因为如果你用一个大文件运行这个文件,你会看到在帖子到达结尾之前弹出“Posting complete”消息。这是我尝试向ThreadPool添加方法调用时遇到的问题,这就是为什么我切换到了Parallel.Foreach。 –
Parallel.Foreach阻止执行,直到所有线程完成,这正是我想要的,但不幸的是,它也阻止了UI的更新。因此我的困境。 –
为你增加了背景工作者的想法...祝你好运! –
你应该使用BackgroundWorker类,请参阅:How to use a BackgroundWorker?
并使用BackGroundWorker.RunWorkerComplete
当线程完成
我正在调查这是一个可能的解决方案。感谢您的建议。 –
背景工人:
**Backgroundworker (System.ComponentModel)**
BackgroundWorker loader = new BackgroundWorker();
loader.DoWork += load_Specials_BW_Thread;
loader.WorkerReportsProgress = true;
loader.ProgressChanged += load_Special_Feeds_Progress_Changed;
private void load_Specials_BW_Thread(object sender, DoWorkEventArgs e)
{
int pctComplete = (int)Math.Floor(ptComplete * 100);//recs done/total recs
(sender as BackgroundWorker).ReportProgress(pctComplete);
}
祝你好运!
你是为进度条提供价值的人 - 所以当你达到100时,请调用你想做的方法。首先,你不需要TaskFactory。 –
第三 - 有多少条线?看起来像显示一个消息框会比处理本身花费更多的时间 –
您可以编写'ValueChanged'事件并检查'Value == Maximum' – TaW