iT邦幫忙

0

C# windows form 在控件事件觸發迴圈 為何不會更新UI

一般在寫 windows form 程式時
如果不是大型開發
老闆只要求 東西能動 項目立刻好
我們可能就會把 邏輯寫在控件事件內 像是

button1_Click(object sender, EventArgs e)

如果控件事件內塞了迴圈 迴圈內的又希望能更新UI顯示的內容 如下寫法會失敗

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Control_Refresh_1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 11; i++)
            {
                Thread.Sleep(200);
                textBox1.Text = i.ToString();
            }
        }
    }
}

上面的例子 迴圈完全跑完才會顯示10 中間的數字完全沒出現
https://ithelp.ithome.com.tw/upload/images/20210302/20129372IUAdmQ5nym.png

如果希望迴圈在跑的過程 就更新UI 就需要使用

Control.Refresh 方法

強制控制項使其工作區失效,並且立即重繪其本身和任何子控制項。

實際使用就是在對的地方加上

this.Refresh();

完整程式碼如下

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Control_Refresh_1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 11; i++)
            {
                Thread.Sleep(200);
                textBox1.Text = i.ToString();
                this.Refresh();
            }
        }
    }
}

參考文章

https://www.ez2o.com/Blog/Post/csharp-Object-Refresh
https://www.cnblogs.com/aooyu/archive/2012/04/12/2444395.html


1 則留言

1
bantime
iT邦新手 5 級 ‧ 2021-03-03 21:01:13

主要是刷新UI是主執行緒 你Thread.Sleep阻塞住主執行緒 所以不會更新

可以採用非同步方式 不阻塞主執行緒

button.Click += async (s, e) =>
{
    for (int i = 0; i < 11; i++)
    {
        await Task.Delay(200);
        text.Text = i.ToString();
    }
};
看更多先前的回應...收起先前的回應...

可是把Thread.Sleep拿掉 看起來也不會更新
似乎 只有等迴圈跑完才更新一次

bantime iT邦新手 5 級 ‧ 2021-03-05 13:35:55 檢舉

進入 button1_Click的還是主執行緒

主執行緒要跑完迴圈才會有空繼續往下做事情

我把我這邊寫法的迴圈展開給你看

await Task.Delay(200);//進來的執行緒休息去做其他事情(不一定是主執行緒)
text.Text = 0.ToString();
await Task.Delay(200);//進來的執行緒休息去做其他事情(不一定是主執行緒) 執行完這行畫面才會更新為0 因為主執行緒是有空的
text.Text = 1.ToString();
await Task.Delay(200);//進來的執行緒休息去做其他事情(不一定是主執行緒) 執行完這行畫面才會更新為1 因為主執行緒是有空的
text.Text = 2.ToString();
await Task.Delay(200);//進來的執行緒休息去做其他事情(不一定是主執行緒) 執行完這行畫面才會更新為2 因為主執行緒是有空的
text.Text = 3.ToString();
await Task.Delay(200);//進來的執行緒休息去做其他事情(不一定是主執行緒) 執行完這行畫面才會更新為3 因為主執行緒是有空的

謝謝 我研究看看

bantime iT邦新手 5 級 ‧ 2021-03-05 13:54:27 檢舉

你可以把原button1_Click內容改寫成這樣

我不讓主執行緒跑計數器

新創建一個執行緒來跑

但是因為不在主執行緒內不能更新UI

所以要將更新UI的事情轉回去給主執行緒(this.Invoke)

var thread = new Thread(() =>
{
    for (int i = 0; i < 11; i++)
    {
        Thread.Sleep(200);
        this.Invoke(new MethodInvoker(() => 
        {
            text.Text = i.ToString();
        }));
    }
});
thread.Start();

我要留言

立即登入留言