Most popular

วันเสาร์ที่ 11 มิถุนายน พ.ศ. 2554

C# ทำ Waiting Dialog ทำให้โปรแกรมดูเป็นมืออาชีพขึ้น

ผู้เขียนขอเดาว่าท่านๆคงจะรู้ดีว่า User หรือผู้ใช้ของเรานั้น บางทีก็เรื่องมาก บางทีก็หงุดหงิดง่าย ยิ่งเจอโปรแกรมที่มี Process แบบเต่าๆแล้วละก็ ผู้ใช้ของเราจะหน้ายับทันที แต่ด้วยบางครั้งการสร้างโปรแกรม ไม่ใช่การร่ายมนต์ ทำทุกอย่างได้สารพักนึก แต่เป็นการสะท้อนความเป็นจริง ในบางครั้ง Process มันก็มากมายเสียจริง จนเป็นไปไม่ได้ที่จะทำอะไรให้เร็วปรู๊ดปร๊าด
ในทางจิตวิทยาแล้วผู้พัฒนาโปรแกรมควรให้ความสำคัญในส่วนนี้มากพอสมควรเลยทีเดียว จริงๆแล้วในวงการนักพัฒนา Software มีข้อห้ามที่รู้ๆกันดีว่าในสิ่งที่ไม่ควรให้ User ได้เห็นคือ

  1. ห้ามมี Error Message แบบที่ไม่คาดคิด ข้อนี้รุนแรงที่สุด เพราะผู้พัฒนาอาจจะไม่สามารถอธิบายได้
  2. ถ้าการทำงานได้ผลผิดพลาด ก็ควรจะมีเหตุผลที่ดี พูดให้ User ฟังได้ลื่นหู (แก้ตัวให้ขึ้นนั่นแหละ)
  3. ไม่ควรให้ User รอการทำงานของโปรแกรมนาน โดยที่โปรแกรมของเราดูแปลกๆ ไม่รู้ว่ามันทำงานอยู่หรือ Hang ไปแล้ว ซึ่งวิธีแก้ไม่ยาก ต้องมีอะไรขยับ หรือมีสถานะให้ User เห็น
การทำ Waiting Dialog รูปแบบนึงในการแก้ปัญหา และทำให้ User มีความสบายใจ และทำให้โปรแกรมที่เราพัฒนาขึ้นดูเป็นมืออาชีพ
สำหรับมือใหม่ ผู้เขียนจะสาธิตการสร้าง Waiting Dialog ง่ายๆ เพื่อความเข้าใจและนำไปประยุคใช้ได้ เริ่มแรกก็คือการสร้าง Project Windows Application ขึ้นมา โดย Project นี้ผู้เขียนจะใช้ฟอร์มเพียง 2 ฟอร์ม คือ ฟอร์มหลัก ชื่อ frmMain และ และฟอร์มที่จะใช้เป็น Waiting Dialog ชื่อ DlgWait

frmMain ในมุมมองออกแบบประกอบด้วย
Panel   > panel1 : ใช้เป็น Container ของปุ่ม
Button > RUN   : ปุ่มกดเพื่อเริ่มทำงาน
Button > Cancel : ปุ่มกดเพื่อหยุดทำงาน
Panel   > panel2 : ใช้เป็น Container ของ DataGridView

frmMain
DlgWait ในมุมมองออกแบบประกอบด้วย
Label > label1 : ข้อความ "รอสักครู่..."
progressBar > progressBar1 : ใช้สำหรับแสดงสถานะการทำงาน

DlgWait
ในส่วนของ DlgWait ผู้เขียนไม่ได้ใส่ Code การทำงานใดๆ แต่กำหนดค่าลงไปใน Constructor เท่านั้น

    public partial class DlgWait : Form
    {
        public DlgWait()
        {
            InitializeComponent();
            //กำหนดรูปแบบของฟอร์มเป็นแบบ none
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            //กำหนดให้คุณสมบัติของฟอร์มเป็น topmost ในตอนแสดงจะอยู่เหนือทุกอย่าง
            this.TopMost = true;
            //รูปแบบของ progressBar ให้เป็นแบบ Marquee (วิ่งไปมา)
            this.progressBar1.Style = ProgressBarStyle.Marquee;
        }
    }

คราวนี้มาถึงการทำงานที่อยู่ใน frmMain ผู้เขียนจะให้มีการทำงานคือ สุ่มชื่อเพื่อนๆขึ้นมาแสดงบน DataGridView โดยที่จะทำการสุ่มอายุด้วย โดยอายุที่จะเป็นเพื่อนได้น่าจะ - หรือ + 10 ปี

ภาพรวมของ Class frmMain
อธิบาย Code ทั้งหมด
        //ส่วนของ Member
        private DataGridView dgv;
        private BindingSource BS;
        private DataSet DS;
        private DataTable TB;
        private Random random;

        //กำหนดค่าคงที่เป็นอายุไว้
        private const int myAge = 30;
        //ค่าของการยกเลิกเป็น false
        private bool cancel=false;

ภายใน Constructor จะเป็นการสร้างทรัพยากรทั้งหมดที่ต้องใช้ คือ การสร้าง DataSet และ DataTable ที่มี Column พร้อมใช้งาน การสร้าง DataGrideView เพื่อแสดงข้อมูล
        public frmMain()
        {
            InitializeComponent();
            //สร้าง Object ในการสุ่มค่า
            this.random = new Random();

            //สร้าง DataTable ชื่อ WAITDATA
            this.TB = new DataTable("WAITDATA");
            //สร้าง Column ให้ DataTable 3 Column คือ ID , FRIEND_NAME และ FRIEND_AGE
            this.TB.Columns.Add(new DataColumn("ID", System.Type.GetType("System.Int32")));
            this.TB.Columns.Add(new DataColumn("FRIEND_NAME",System.Type.GetType("System.String")));
            this.TB.Columns.Add(new DataColumn("FRIEND_AGE", System.Type.GetType("System.Int32")));

            //สร้าง Dataset เพื่อเก็บ DataTable
            this.DS=new DataSet();
            this.DS.Tables.Add(this.TB);

            //สร้าง BindingSource ส่งผ่านข้อมูลกับ DataSet
            this.BS = new BindingSource(this.DS, "WAITDATA");

            //สร้าง DataGridView ให้อยู่ใน panel 2 ที่ออกแบบไว้แล้ว พร้อมระบุให้ใช้ข้อมูลผ่าน BindingSource ชื่อ BS
            this.dgv = new DataGridView();
            this.dgv.Dock = DockStyle.Fill;
            this.dgv.Parent = this.panel2;
            this.dgv.MultiSelect = false;
            this.dgv.ReadOnly = true;
            this.dgv.DataSource = this.BS;
        }

method ต่อมาที่สร้างคือ getRandonFriend เป็น method ที่จะคือค่าจากการสุ่ม พร้อมกัน 2 ค่า คือ ชื่อ และอายุ โดยใช้ Parameter แบบ Pass by reference

        private void getRandonFriend(ref string friendName,ref int friendAge)
        {
            string[] friend = {"เอกศักดิ์","สมชาย","วิชัย","ศศิชา","กัญญาลักษณ์","ชูวิทย์","สุพจน์","รวีวรรณ","ธีรพล" };

            //สุ่มชื่อเพื่อน และอายุ ส่งไปที่ reference variable
            friendName = friend[this.random.Next(friend.Length)];
            friendAge = this.random.Next(myAge-10, myAge + 10);
        }

สุดท้ายคือ method ซึ่งก็คือ Event ปุ่ม Run และ Cancel

        private void RUN_Click(object sender, EventArgs e)
        {
            this.cancel = false;
            this.RUN.Enabled = false;

            //สร้าง Waiting Dialog ขึ้นมา
            DlgWait w = new DlgWait();
            //สั่งให้โชว์
            w.Show(this);
            
            try
            {
                //วนลูปเพื่อสุ่มรายชื่อเข้าใส่ตาราง
                for (int i = 0; i < 100000; i++)
                {
                    //หากสถานะ cancel เป็น true หมายความว่าให้หยุดการทำงานทันที
                    if (this.cancel) break;

                    Application.DoEvents();

                    //ประกาศตัวแปรเพื่อไปรับค่า
                    string friendNameValue = null;
                    int friendAgeValue = 0;

                    //สุ่มข้อมูลด้วย method getRandonFriend
                    this.getRandonFriend(ref friendNameValue, ref friendAgeValue);

                    // นำค่าสุ่มที่ได้มาเพิ่มเป็นข้อมูลใหม่ใน DataTable
                    DataRow dr = this.TB.NewRow();
                    dr["ID"] = i + 1;
                    dr["FRIEND_NAME"] = friendNameValue;
                    dr["FRIEND_AGE"] = friendAgeValue;
                    this.TB.Rows.Add(dr);

                    เมื่อลูปทำงานไป Waiting Dialog ก็จะ Update ตัวเองไปด้วย
                    w.Update();
                }
            }
            finally
            {
                //เมื่อทำงานเสร็จสิ้น Waiting Dialog ก็หมดความหมาย ต้องทำลายทิ้ง
                w.Close();
                w.Dispose();
                this.RUN.Enabled = true;
            }
        }

        private void Cancel_Click(object sender, EventArgs e)
        {
            //ให้สถานะของ cancel เป็น true การทำงานจะได้หยุด
            this.cancel = true;
            this.RUN.Enabled = true;
        }

เมื่อทดสอบโดยกดปุ่ม Run โปรแกรมก็จะทำงาน พร้อมกับแสดง Waiting Dialog ให้ User เห็น


1 ความคิดเห็น: