使用控制項的BeginUpdate功能

使用BeginUpdate,減少控制項的重繪頻率,降低系統的負擔.

開始在學習的時候,就知道不可以直接拿UI上的控制項來做運算的動作,例如:

for (int i=0;i<100;i++)
{
    textBox1.Text+=i;
}
  這樣跑下來的效能就差的可怕,因為每更新一次值,就會重繪控制項,而且還會造成畫面的閃動,所以就以上面這個例子,應該先把值存到一個變數內,等運算完成,再把最後結果的變數存至textBox1去.
  但今天如果控制項必需一個一個加進去時,要怎麼辦?例如listbox,是有AddRange可以使用,可是有些狀況必需要用Add,不能用AddRange的方式,這時就要用控制項的BeginUpdate/EndUpdate的方式來處理,基本上AddRange與用Add加上BeginUpdate做法的效能異不大,但與用Add而未加BeginUpdate的方式比較起來,差異就很大了,以下列的程式碼來比較出它們的效能差異.
 
方式1 : 用Add的方式,不使用BeginUpdate
 
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
    listBox1.Items.Add(i);
}

sw.Stop();
MessageBox.Show(string.Format("耗時(毫秒) : {0}", sw.ElapsedMilliseconds.ToString("#,##0")));
 
方式2: 用Add的方式,使用BeginUpdate
 
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
listBox1.BeginUpdate();
for (int i = 0; i < 10000; i++)
{
    listBox1.Items.Add(i);
}

listBox1.EndUpdate();
sw.Stop();
MessageBox.Show(string.Format("耗時(毫秒) : {0}", sw.ElapsedMilliseconds.ToString("#,##0")));
 
方式3: 用AddRange的方式
 
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
List<object> lt = new List<object>();
sw.Start();
for (int i = 0; i < 10000; i++)
{
    lt.Add(i);
}

listBox1.Items.AddRange(lt.ToArray());
sw.Stop();
lt.Clear();
MessageBox.Show(string.Format("耗時(毫秒) : {0}", sw.ElapsedMilliseconds.ToString("#,##0")));
 
各自執行後的結果如下:
方式1: 950毫秒
方式2: 305毫秒
方式3: 307毫秒
 
  2跟3的差異很小,但都跟方式1差了3倍以上,或許有些朋友的想法是我又不能可會加上那麼多筆資料到listbox裡去,或是才那麼0點幾秒,沒差啦,但有些使用者的硬體設備沒有那麼好,從他們的環境比較起來,可能就不是0點幾秒了,而且系統後續的運用也不見得能如當初所預期的方式去用,小弟就有個例子,當初有個作業在規劃時,評估這個Treeview不會有太多節點,最多幾百個,但後來公司的經營策略調整,這個Treeview瞬間爆增到數萬個節點,硬體設備差一點的,可以中午去吃個午餐,睡個午覺,下午應該就可以用了.
  所以在做此類動作時,除了ListBox,其它如ComboBox,TreeView等,要記得使用BeginUpdate及EndUpdate,避免控制項過度頻繁的重繪,除了造成畫面的閃動外,也增加了系統的負擔.
 
MSDN 說明 :
  將多重項目加入至 ListBox 的慣用方法是使用 ListBox.ObjectCollection 類別的 AddRange 方法 (透過 ListBox 的 Items 屬性)。這可讓您在單一作業中將項目陣列加入至清單中。但如果您要使用 ListBox.ObjectCollection 類別的 Add 方法一次加入一個項目,可以使用 BeginUpdate 方法在每次項目加入至清單時,防止控制項重繪 ListBox。當您完成將項目加入至清單的工作後,請呼叫 EndUpdate 方法以便使 ListBox 進行重繪。這種加入項目的方法可防止當大量項目加入至清單時,出現 ListBox 閃動繪製。