ใช้งาน BackgroundWorker กับ ProgressBar ใน C#

หลายๆ คนคงเคยเขียนโปรแกรม Windows Form แล้วเวลาจะ Update พวก UI Control ต่างๆแล้วเจอปัญหาว่าไม่สามารถ Update Control เหล่านั่นได้หรือ Control เหล่านั่นไม่สามารถ Update ค่าได้ตามที่เราต้องการ ผมขอยกตัวอย่างง่ายๆ สักตัวอย่างนะครับที่หลายคนอาจจะเคยเจอกันมาบ้างแล้ว สมมุติว่าผมต้องการเขียนโปรแกรมตัวหนึ่งที่ต้องการนับจำนวนเลขจาก 1 - 10 โดยนับการนับวินาทีละครั้งนะครับ แล้วผมต้องการที่จะให้มี ProgressBar เพื่อแสดงผการทำงานด้วยนะครับ เรามาลองดูว่าทำยังไงกันดีกว่าครับ

สร้างโปรเจ็คกันก่อนเลยครับโดยผมให้ชื่อว่า TestProgressBar




จากนั่นให้เราวาง Control ก่อนนะครับโดยเราจะใช้ Control ดังนี้นะครับ Button, Label และ ProgressBar ตามภาพด้านล่างเลยนะครับ


ก่อนที่เราจะทำการเขียนโค็ดนะครับ ผมมีอะไรจะบอกก่อนนะครับเรื่องการรันโปรแกรม ถ้าผู้อ่านเคยเขียนโปรแกรมบน Console หรือโปรแกรมที่หน้าจอดำๆ นะครับ อันนั่นเละครับโปรแกรมที่เขียนจะเป็นการทำงานแบบ Single Thread คือการทำงานที่ต่อเนื่องกันในที่เดียวกันเลยไม่มีการถูก Interrupt ด้วยเหตุนี้เองทำให้เราไม่มาสารถเขียนโปรแกรมบน Console ได้แบบ Multithread ไม่เหมือนกับ Windows Form ที่ซึ่งเราจะมี UI Control หลากหลายใน Form ของเราทำให้เราสามารถเขียนโปรแกรมแบบ Multithread ได้

แล้ว Multithread จะช่วยเราได้ยังไง แน่นอนครับมันช่วยเราได้อย่างมากเลยครับ ผมขออธิบายง่ายๆ นะครับว่าถ้าเราเขียนโปรแกรม เช่น การบวกค่าเลขไปเรื่อยๆ แต่ในระหว่างนั่นเราจำเป็นจะต้อง Update ProgressBar ของเราด้วยถ้าเราเขียนโปรแกรมด้วย Single Thread เราจะไม่สามารถ Update ProgressBar ได้นะครับเพราะว่าการที่เราจะทำการ Update พวก UI Control ได้นั่นเราจะต้องอาศัยการทำงานของอีก Thread หนึ่งในการ Update เลยนะครับ นั่นเองถึงเป็นเหตุให้เราต้องทำการสร้าง Thread ขึ้นมาเพื่อทำการ Update Control โดยเฉพาะไงครับ

แต่ทว่าการเขียนโปรแกรมด้วย Thread ใน .Net นั่นถึงจะง่ายแต่ก็ยังมีความยุ่งยากอยู่ในเรื่องของการจัดการ ดังนั่น .Net จึงได้เตรียม Class ที่ชื่อว่า BackgroundWorker ขึ้นมาเพื่อทำหน้าที่อะไรก็ตามที่ต้องแตก Thread ออกไปทำงานโดยเฉพาะเลย จึงทำให้ Programmer เขียนโปรแกรมได้ง่ายขึ้นมาก จากรูปด้านล่างนั่นหลักการทำงานของ BackgroundWorker นั่นทำงานง่ายๆ แค่สั่งเซ๊ตอัพครั้งแรกก่อนจากนั่นก็สั่งให้มัน DoWork เมื่อเราสั่งมันทำงานก็จะมีการสร้าง Thread ขึ้นมาใหม่อีกตัวที่แยกออกไปจาก Main Program เลยจากนั่นตัว Thread ที่เราสร้างมาก็จะรายงาน Progress ของมันเรื่องให้แก่เรานะครับว่ามันทำงานถึงไหนแล้ว จนกะทั่งสุดท้ายเมื่อมันทำงานเสร็จแล้ว Thread นั่นก็จะรายงายให้เราทราบเมือนกันครับ


เอาละครับเรามาลงมือเขียนโค็ดกันเลยดีกว่าครับ ก่อนอื่นเลยเราต้องทำงานกำหนดค่าเริ่มต้นให้แก้ Control ที่เราเพิ่มเข้าไปก่อนนะครับ โดยเราจะเขียนโค็ดใน Form1_Load ครับจุดที่น่าสนใจสำหรับส่วนนี้มีดังนี้ครับ

บรรทัดที่ 26 - 28 นั่นจะเป็นการกำหนดค่าเริ่มต้นให้ ProgressBar ครับโดยกำหนดว่าให้เพิ่มที่ละ Step โดยเพิ่มครั้งละ 1 โดยที่ค่าน้อยสุดที่เป็นไปได้ของ ProgressBar และค่ามากสุดคือ 0 - 10 

บรรทัดที่ 30 - 32 คือการกำหนดค่าให้กับ BackgroundWorker นะครับโดยเรากำหนดว่าเมื่อเราสั่งให้ BackgroundWorker ทำงานนั่นจะมา Call Functรon ไหนของเรานะครับอย่างในตัวอย่างนั่นเราสั่งมันว่าเมื่อตัวมันเองเริ่มทำงานนั่น Function bgwork_Dowork จะถูก Call ด้วยครับส่วน ProgressChanged นั่นจะถูก Call ก็ต่อเมื่อเรามีการเปลี่ยน Step value ค่า ProgressBarนั่นเองครับสำหรับอันสุดท้าย RunWorkerCompleted ชัดเจนอยู่แล้วครับว่าจะถูก Call ก็ต่อเมื่อ Thread นั่นทำงานเสร็จแล้วนะครับ


ต่อจากนี้เรามาใส่โค็ดใน DoWork กันดีกว่าครับในส่วนนี้เราจะเขียนโปรแกรมให้วนลูปเพื่อนับเลขจำนวน 1 - 10 นะครับเมื่อเรานับเสร็จเราก็สั่งรายงาน Progress นะครับแล้วตัวนี้เองละครับที่จะไป Call  ProgressChanged ไงละครับ Thread.Sleep นั่นคือคำสั่ง Delay 1 วินาทีนั่นเองครับ


ที่นี้เรามาดูส่วนของ ProgressChanged กันมั่งดีกว่าครับ ในส่วนนี้เราก็แค่ Update ค่าในแก้ ProgressBar และ Label เท่านั่นเองครับแต่จะมีนิดหนึ่งคือเราไม่สามารถสั่ง Update ไปที่ Control ได้โดยตรงครับเราจำเป็นที่จะต้องการใช้งานการ Call Function แบบ Invoke Function ครับอันนี้เป็นเรื่องทางเทคนิคการใช้การ Resource ของโปแกรมที่รันแบบ Multithread ครับ เอาเป็นว่าคร่าวๆ ว่าต้อง Call รูปแบบนี้นะครับ (อธิบายยาวเดียวกลัวจะงงครับ)


มาถึงตอนเวลาจบแล้วครับส่วนของ RunWorkerCompleted ส่วนนี้ไม่มีอะไรมากครับถ้า BackgroundWorker ทำงานเสร็จ Function นี้ก็จะถูก Call เองอัตโนมัติครับ ในนี้เราก็แค่ใส่ MessageBox ไว้เท่านั่นเองครับ


มาถึงตอนที่เราจะสั่งให้ BackgroundWorker ทำงานแล้วนะครับโดยเราออกแบบไว้ว่าเมื่อเราคลิ๊กปุ้มก็จะให้ BackgroundWorker ทำงานดังนั่นเราจะต้องสั่งรัน RunWorkerAsync เพียงเท่านี้เองครับ


เห็นมั้ยครับว่าไม่ได้ยากอะไรเพียงแค่เขียนโค็ดนิดหน่อยเราก็สามารถรายงานสถานะการทำงานของโปรแกรมของเราได้แล้วครับ
ถ้าอ่านแล้วชอบอย่าลืมช่วยกด Like Fan Page ที่ http://www.facebook.com/PStudioDev ด้วยนะครับ ขอบคุณครับ