C#在其他线程中修改UI 举例

本想翻译来着,后来想想算了,一是比较懒,二是没时间,三是觉得没必要。有需要的话评论原文地址:http://kristofverbiest.blogspot.sg/2007/02/simple-pattern-to-invoke-gui-from.html


If you are making a GUI application and you are using multiple threads, there is one very important rule: GUI controls can only be accessed from the GUI thread. This is inherent to Windows development and is not a limitation of .NET.

Let me first say that although this seems to be an annoyance for the developer, this is actually a great thing! It means that while you are developing a GUI application you generally don't have to worry about threading issues (locks, deadlocks) because you know that all GUI-access is done from a single thread.

But of course there are situations where you want to start background processing on a seperate thread and access the GUI from this thread. Take for instance this simple example:

 
   
public void SetTitleUnsafe( string title) { // When invoked from another thread, this next statement is illegal: this .Text = title; }

If you call this method from a thread that is not a GUI thread, everything may seem to go well at first sight. But because we violated the very important rule, the behaviour of our application is now undefined. Things may (and will) start to go wrong very unpredictably, sometimes much later when there is no obvious relationship with the violation that was made. This makes this problem very hard to find. Among the things that could happen is GUI-events to become 'lost' and the GUI to become unresponsive.

Luckily, form Visual Studio 2005 onward you get a nice error-message when you violate this rule while the debugger is attached:

So now you at least get an immedeate notification that you made a mistake. This one of the reasons why I advise to run your code from Visual Studio (using F5) while you are developing.

To get around our threading-violation, Winforms provides these helper-methods: BeginInvoke, EndInvoke, Invoke, InvokeRequired. But even with these methods available it may not be obvious how to use them in a correct and simple way.

That's why I present this pattern:

 
   
delegate void Invoker( string parameter); public void SetTitleSafe( string title) { if ( this .InvokeRequired) { // Execute the same method, but this time on the GUI thread this .BeginInvoke( new Invoker(SetTitleSafe), title); // we return immedeately return ; } // From here on it is safe to access methods and properties on the GUI // For example: this .Text = title; }

As you can see, you will need to define a delegate that matches your method (or use an existing delegate that is defined elsewhere, such as the System.Windows.Forms.MethodInvoker). This is how the pattern works:

  • When the method is called from a thread that is not the GUI thread, InvokeRequired will be true. The method will be wrapped in a delegate and passed to the BeginInvoke method (together with the parameters). We return immedeately. BeginInvoke guarantees that some time later our method will be called on the GUI thread.
  • When the method is called on the GUI thread, InvokeRequired returns false so we just go forward and access the GUI in any way we like.

This is in my opinion the simplest and shortest pattern that is always correct.

From .NET 2.0 onwards the same pattern can be written using an anonymous method but that is less readable in my opinion, so I prefer to keep this pattern.

Also in .NET 2.0 you can use the BackgroundWorker class that handles all the details behind your back. But the same principle still applies: never access GUI from another thread!

第一条评论的方法也可以:

I prefer a small helper class like this one, it makes the code even cleaner and shorter:

using System.Windows.Forms;

public static class ControlsHelper
{
    public static void SyncBeginInvoke(control, MethodInvoker del)
    {
        if ((control != null) && control.InvokeRequired)
            control.BeginInvoke(del, null);
        else
            del();
    }
}

private void SetLabelText(int number)
{
    ControlsHelper.SyncBeginInvoke(this, delegate()
        {  
            label.Text = number.ToString();
        });
}

你可能感兴趣的:(C#)