简体   繁体   中英

Control.BeginInvoke() not working without MethodInvoker - But why?

Ich have a little WinForms program, which has 1 Button and 1 Textbox. If i click the button then the programm counting from 1 to 100000 and shows in every step the current time in milliseconds in the textbox. The countingloop is running in a seperate thread.

                public partial class Form1 : Form {

                   public delegate void myDelegate();
                   public myDelegate mydelegate;

                    public Form1() {
                        InitializeComponent();

                        mydelegate = new myDelegate(b);
                    }

                    private void button1_Click(object sender, EventArgs e) {
                        button2.Focus();

                        Thread t = new Thread(a);
                        t.Start();
                    }

                    private void Form1_KeyDown(object sender, KeyEventArgs e) {
                        Console.WriteLine(e.KeyCode);
                    }

                    public void a() {
                        for (int i = 0; i < 100000; i++) {
                            textBox1.BeginInvoke(mydelegate);



                        }
                    }

                    public void b() {
                        textBox1.Text = GetCurrentMilli().ToString();
                        textBox1.Refresh();
                    }

                    public static double GetCurrentMilli() {
                        DateTime Jan1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                        TimeSpan javaSpan = DateTime.UtcNow - Jan1970;
                        return javaSpan.TotalMilliseconds;
                    }

                }

If i run this, the program works, but the gui is freezing till the loop is finished. But why? I have called BeginInvoke?!

If i replace

                textBox1.BeginInvoke(mydelegate);

with

                      textBox1.Invoke(new MethodInvoker(b));

then it works without any freeze or problems. But why?

When you call BeginInvoke you are scheduling a UI update to happen, and then continuing along with your program without waiting for that UI update to happen. When you do this just a few times you're fine, but the problem that you're having is that you're sending in 100,000 requests all at once, and it's going to take the UI some time to get through all of those requests, and nothing else is going to be able to be done in that time because any new UI updates go to the end of the line, and won't be performed until the other requests are finished.

While there are ways to keep your general approach the same and try to let other operations cut to the front of the line, the proper approach is to avoid the problem in the first place. You have no need to be sending 100,000 updates to single textbox at once.

If you want the textbox to have the appearance of a clock, in which it ticks up, then a Timer would be a good tool for the job; you can handle the Tick event to update the textbox every second, quarter second, or some other more "human time" interval.

If the idea is to update the UI with the progress of some long running operation, then you simply want to ensure that you don't update progress quite so often. Update progress every few dozen iterations of your loop, instead of every single one, for example.

You may need to call UpdateLayout in between, not sure if it needs to be invoked to prevent cross-thread exception

public void a() {
    for (int i = 0; i < 100000; i++) {
        textBox1.BeginInvoke(mydelegate);
        textBox1.UpdateLayout();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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