简体   繁体   English

如何检查线程,然后填写C#/ WPF中的进度条

[英]How to check a thread is done, then fill progress bar in C# / WPF

I am just working on my first GUI application on Windows. 我正在开发我在Windows上的第一个GUI应用程序。

I have a WPF GUI to a small C# utility which copies files. 我有一个WPF GUI到一个复制文件的小型C#实用程序。 When the button is clicked to copy, I obviously don't want the GUI to hang. 单击按钮进行复制时,我显然不希望GUI挂起。 So, I fire off a new thread to run the method which copies the files. 因此,我启动一个新线程来运行复制文件的方法。 I assume I'm on track so far and there's no "better" way of doing it in C#? 我认为到目前为止我已经走上正轨,并且在C#中没有“更好”的方式吗?

Now, I have a ProgressBar which I want to appear filled when the thread is done. 现在,我有一个ProgressBar ,我希望在线程完成时显示它。 (It's fine running as indeterminate for now). (现在它运行良好,不确定)。 How do I check when the copying is done? 如何检查复制何时完成?

So, so far I have: 所以,到目前为止,我有:

Thread t = new Thread(delegate() 
{ 
    po.Organise(inputPath, outputPath, recursive); 
});

t.Start();

PBar.IsIndeterminate = true;

And I want something after that that works like: 之后我想要的东西就像:

if (t.Done)
{
    PBar.Value = 100;
}

Have a look at the BackgroundWorker class. 看看BackgroundWorker类。 It supports events like RunWorkerCompleted or ProgressChanged . 它支持RunWorkerCompletedProgressChanged等事件。
Have a look here , too (this is about threading in general + backgroundworker, again). 看看这里 (这是关于一般的线程+后台工作者,再次)。

As already stated, consider the use of the BackgroundWorker class. 如前所述,请考虑使用BackgroundWorker类。 It was designed for these situations and exposes events suited for what you are trying to accomplish. 它专为这些情况而设计,并展示适合您要完成的事件。

Use the ProgressChanged event to report progress incrementally and the RunWorkerCompleted for when the task finishes. 使用ProgressChanged事件以递增方式报告进度,并在任务完成时使用RunWorkerCompleted Check the MSDN page for code samples. 检查MSDN页面以获取代码示例。

You need a callback method. 你需要一个回调方法。 This should get you started. 应该让你开始。 It uses an AsyncCallback, which is the best way to tackle this type of issue. 它使用AsyncCallback,这是解决此类问题的最佳方法。

I just looked up an example I've been using for a project and stripped out the code specific to my app: 我刚刚查看了一个我一直用于项目的示例,并删除了特定于我的应用程序的代码:

System.Windows.Forms.MethodInvoker mi = new System.Windows.Forms.MethodInvoker(delegate()
{
    // Do your file copy here
});

AsyncCallback ascb = new AsyncCallback(delegate(IAsyncResult ar)
{
    this.Dispatcher.Invoke(new ThreadStart(delegate (){
    // set progressbar value to 100 here
    }), null);
});

mi.BeginInvoke(ascb, null);

Wrap the if (t.Done) block in its own method. 用自己的方法包装if (t.Done)块。 Invoke this method from the end of your worker thread. 从工作线程的末尾调用此方法。

Also, you might want to give the worker thread a name to make it easier to spot in the debugger. 此外,您可能希望为工作线程指定一个名称,以便更容易在调试器中找到它。

The quick and easy hack would be to just update the UI at the end of your anonymous method in your thread. 快速简单的黑客就是在线程中的匿名方法结束时更新UI。 Obviously you can't update it directly, but you can use Dispatcher.Invoke : 显然你无法直接更新它,但你可以使用Dispatcher.Invoke

Thread t = new Thread(delegate() 
{ 
    po.Organise(inputPath, outputPath, recursive); 
    Dispatcher.Invoke(new Action(()=>{PBar.Value = 100;}),null);
});

t.Start();

As a general Windows programming principal, you have to make calls to update the UI from the UI thread (the one that is processing messages through a message pump). 作为一般的Windows编程主体,您必须调用从UI线程(通过消息泵处理消息的线程)更新UI。

In Windows Forms, the way that this was done was through the implementation of the ISynchronizeInvoke interface on the Control class, primarily through the implementation of the Invoke method . 在Windows窗体中,完成此操作的方式是通过在Control类上实现ISynchronizeInvoke接口 ,主要通过实现Invoke方法

With the release of .NET 2.0, it was realized that a better mechanism was needed to marshal calls into the correct context. 随着.NET 2.0的发布,人们意识到需要一种更好的机制来将调用编组到正确的上下文中。 That's where the SynchronizationContext comes in. 这就是SynchronizationContext的用武之地。

This class abstracts the interface you would use for marshaling calls to different contexts, allowing for specific implementations depending on the context. 此类抽象出用于编组调用不同上下文的接口,允许根据上下文进行特定实现。

So whether or not Windows Forms is the environment, or WPF, one call can be made in the same way across those contexts with the same effect (marshaling the call). 因此,无论Windows窗体是环境还是WPF,都可以以相同的方式在这些上下文中进行一次调用,并产生相同的效果(编组调用)。

In your particular case, because you are using a closure (anonymous method), you can take advantage of the fact that a SynchronizationContext is available to you (through the static Current property ) at the invocation site of the Thread to provide the mechanism to call back to the UI thread from your background thread: 在您的特定情况下,因为您正在使用闭包(匿名方法),您可以利用在Thread的调用站点上可以使用SynchronizationContext(通过静态Current属性 )来提供调用的机制。从后台线程返回UI线程:

// Get the synchronization context.
// This is in the UI thread.
SynchronizationContext sc = SynchronizationContext.Current;

// Create the thread, but use the SynchronizationContext
// in the closure to marshal the call back.
Thread t = new Thread(delegate()  
{  
    // Do your work.
    po.Organise(inputPath, outputPath, recursive);  

    // Call back using the SynchronizationContext.
    // Can call the Post method if you don't care
    // about waiting for the result.
    sc.Send(delegate()
    {
        // Fill the progress bar.
        PBar.Value = 100;
    });
}); 

// Make the progress bar indeterminate.
PBar.IsIndeterminate = true;

// Start the thread.
t.Start(); 

Note, if you don't care about waiting for the result of the call back to the UI thread, you can make a call to the Post method instead, which will dispatch the call to the UI thread without waiting for that call to complete. 注意,如果您不关心等待回调到UI线程的结果,则可以调用Post方法 ,这将调用UI线程而不等待该调用完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM