Mục tiêu Xử lý kéo thả control (drag & drop) Tạo sử dụng Dialog Sử dụng multi thread (đa luồng) Nội dung Xử lý drag drop Để xử lý kéo thả control (di chuyển control dựa vào việc nắm giữ thông qua thao tác mouse) trước hết cần phân tích hành động di chuyển đồ vật thông thường trước hết cần nắm giữ vật di chuyển (vẫn nắm giữ) đến đích thả Hành động tương đương với mouse nhấn giữ mouse di chuyển thả Như để thực kéo thả dựa vào mouse phải xử lý event: MouseDown (nhấn), MouseMove (di chuyển) MouseUp (nhả) Ngoài dựa vào event mouse nên việc di chuyển control xác tính theo thay đổi vị trí mouse (độ sai khác) thời điểm thời điểm bắt đầu nhấn di chuyển Cụ thể sau: Tạo project với giao diện: PictureBox: pbTest Xử lý event MouseDown, MouseUp MouseMove: public partial class Form1 : Form { public Form1() { InitializeComponent(); pbTest.MouseDown += new MouseEventHandler(pbTest_MouseDown); pbTest.MouseUp+=new MouseEventHandler(pbTest_MouseUp); pbTest.MouseMove+=new MouseEventHandler(pbTest_MouseMove); } //đặt biến cờ để xác định có nhấn chuột hay không bool isDown = false; //biến lưu vị trí ban đầu mouse Point pStartDown = new Point(); void pbTest_MouseDown(object sender, MouseEventArgs e) { //bật cờ lên isDown = true; //lưu lại vị trí bắt đầu pStartDown.X = e.X; pStartDown.Y = e.Y; } void pbTest_MouseUp(object sender, MouseEventArgs e) { //tắt cờ isDown = false; } void pbTest_MouseMove(object sender, MouseEventArgs e) { //kiểm tra mouse có nhấn không if (isDown) { //tính độ sai khác vị trí int dX = e.X - pStartDown.X; int dY = e.Y - pStartDown.Y; //cập nhật độ sai khác cho vị trí control pbTest.Left += dX; pbTest.Top += dY; } } } Áp dụng tạo Custom control tự có drag, drop Thêm class MyDragDropControl vào project (bằng cách add -> Class) Cài đặt override event MouseDown, MouseUp MouseMove public class MyDragDropControl : Control { bool isDown = false; Point pStartDown = new Point(); protected override void OnMouseDown(MouseEventArgs e) { isDown = true; pStartDown.X = e.X; pStartDown.Y = e.Y; base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { isDown = false; base.OnMouseUp(e); } protected override void OnMouseMove(MouseEventArgs e) { if (isDown) { int dX = e.X - pStartDown.X; int dY = e.Y - pStartDown.Y; Left += dX; Top += dY; } base.OnMouseMove(e); } } Biên dịch chương trình, MyDragDrop xuất ToolBox, kéo vào form sau: Chạy chương trình để thấy thành Tạo sử dụng Dialog Khi sử dụng Dialog chủ yếu nhằm mục đích bắt buộc người dùng phải lựa chọn phải nhập thông tin cần thiết Về chất Dialog Form bình thường, khác cách hiển thị để phù hợp với nội dung Giả sử yêu cầu tạo Dialog bắt buộc lựa chọn thể loại game mở đầu cảu Minesweeper Đầu tiên tạo custom control dạng Button với cách thể có hiệu ứng lựa chọn public class MyButton : Button { //giá trị màu text hover Color colorHover = Color.Blue; public Color ColorHover { get { return colorHover; } set { colorHover = value; } } //để lưu màu text mặc định Color colorNormal; public MyButton() { //thiết lập giao diện phẳng FlatStyle = FlatStyle.Flat; FlatAppearance.BorderSize = 0; } //tạo giao diện mouse rê vào protected override void OnMouseHover(EventArgs e) { FlatAppearance.BorderSize = 1; FlatAppearance.BorderColor = Color.DimGray; //giữ lại màu text mặc định colorNormal = ForeColor; //thiết lập màu text hover ForeColor = Color.Blue; base.OnMouseHover(e); } //reset giao diện mouse rời protected override void OnMouseLeave(EventArgs e) { if (!Focused) { FlatAppearance.BorderSize = 0; } else { FlatAppearance.BorderColor = Color.LightPink; } ForeColor = colorNormal; base.OnMouseLeave(e); } protected override void OnGotFocus(EventArgs e) { FlatAppearance.BorderSize = 1; FlatAppearance.BorderColor = Color.LightPink; base.OnGotFocus(e); } protected override void OnLostFocus(EventArgs e) { FlatAppearance.BorderSize = 0; base.OnLostFocus(e); } } Sau tạo Form đặt tên MyDialog Đặt thuộc tính MaximizeBox MinimizeBox false: Đặt thuộc tính FormBorderStyle FixedDialog Tạo giao diện với MyButton sau: MyButton: mbtnBeginner MyButton: mbtnIntermediate MyButton: mbtnAdvanced Cài đặt xử lý tương ứng cho MyDialog public partial class MyDialog : Form { //lưu giá trị trả kết Dialog public string TextOfChoice { get; set; } public MyDialog() { InitializeComponent(); mbtnBeginner.Click += new EventHandler(mbtn_Click); mbtnIntermediate.Click += new EventHandler(mbtn_Click); mbtnAdvanced.Click += new EventHandler(mbtn_Click); FormClosed += new FormClosedEventHandler(MyDialog_FormClosed); } void mbtn_Click(object sender, EventArgs e) { var mbtn = sender as MyButton; //giữ giá trị lựa chọn TextOfChoice = mbtn.Text; //thiết lập kết Dialog DialogResult = DialogResult.OK; //đóng Dialog this.Close(); } void MyDialog_FormClosed(object sender, FormClosedEventArgs e) { //kiểm tra có nhấn button chưa? if (DialogResult != DialogResult.OK) { DialogResult = DialogResult.Cancel; } } } Tạo frmMain với giao diện sau: Label: lblMsg Xử lý event Load form public partial class frmMain : Form { public frmMain() { InitializeComponent(); Load += new EventHandler(frmMain_Load); } void frmMain_Load(object sender, EventArgs e) { MyDialog dlg = new MyDialog(); if (dlg.ShowDialog() == DialogResult.OK) { lblMsg.Text = dlg.TextOfChoice; } else { this.Close(); } } } Chạy chương trình Sử dụng multi thread Mặc định chương trình xử lý từ xuống dưới, xong dòng lệnh (hay hàm) đến hàm Với sưc mạnh phần cứng việc xử lý tác vụ đơn giản hầu hết không chiếm hết tài nguyên, nhiên tác vụ đơn giản thời gian hoàn thành lâu (ví dụ vòng lặp lớn) làm cho chương trình chậm (vì phải chờ) nhiều trường hợp tác vụ sau không cần kết tác vụ chạy gây nên chờ đợi không cần thiết Xét ví dụ sau: giả sử cần thực công việc điền 10,000 ký tự vào TextBox Tạo Form với tên frmMT có giao diện sau: TextBox: txtMsg ToolStripStatusLabel: tssLbl ToolStripProgressBar: tspbProgress TextBox: txtTest Để tạo TextBox nhiều dòng thay đổi thuộc tính Multiline Viết phần xử lý frmMT sau: public partial class frmMT : Form { public frmMT() { InitializeComponent(); Button: btnRun btnRun.Click += new EventHandler(btnRun_Click); } void btnRun_Click(object sender, EventArgs e) { //chạy hàm với yêu cầu điền 10,000 //ký tự vào txtMsg Run(10000); } void Run(int n) { Random rd = new Random(); StringBuilder sb = new StringBuilder(); tspbProgress.Maximum = n; for (int i = 0; i < n; i++) { sb.Append((char)rd.Next((int)'A', (int)'Z')); txtMsg.Text = sb.ToString(); tspbProgress.Value = i + 1; //ép buộc form cập nhật this.Refresh(); } } } Khi chạy chương trình thấy KHÔNG thể nhập vào txtTest hay chí việc đóng Form Để khắc phục vấn đề kể ta sử dụng multi thread nhờ vào class Thread namespace System.Threading Sửa lại phần cài đặt cho frmMT (nếu cần nên tạo form mới) sau: public partial class frmMT : Form { //giữ lại tham chiếu Thread để xử lý //ngừng cần thiết (khi đóng form) Thread thr = null; public frmMT() { InitializeComponent(); btnRun.Click += new EventHandler(btnRun_Click); //xử lý event FormClosed để ngừng thread FormClosed += new FormClosedEventHandler(frmMT_FormClosed); } void frmMT_FormClosed(object sender, FormClosedEventArgs e) { if (thr != null && thr.ThreadState == ThreadState.Running) { thr.Abort(); } } void btnRun_Click(object sender, EventArgs e) { //kiểm tra thread chạy bỏ qua if (thr != null && thr.ThreadState == ThreadState.Running) { return; } if (thr == null) { //tạo Thread cần đưa tham số phương thức //cần thực (phương thức phải kiểu void //nếu có tham số phải tham số kiểu object) thr = new Thread(new ParameterizedThreadStart(Run)); } thr.Start(10000); } //sửa lại cách thức truyền tham số để phù hợp với Thread void Run(object obj) { int n = (int)obj; Random rd = new Random(); StringBuilder sb = new StringBuilder(); tspbProgress.Maximum = n; for (int i = 0; i < n; i++) { sb.Append((char)rd.Next((int)'A', (int)'Z')); txtMsg.Text = sb.ToString(); tspbProgress.Value = i + 1; //ép buộc form cập nhật this.Refresh(); } } } Chạy chương trình nhận xét khác biệt Bài tập Thực hoàn chỉnh ví dụ ... tspbProgress.Value = i + 1; //ép buộc form cập nhật this.Refresh(); } } } Chạy chương trình nhận xét khác biệt Bài tập Thực hoàn chỉnh ví dụ ... hoàn thành lâu (ví dụ vòng lặp lớn) làm cho chương trình chậm (vì phải chờ) nhiều trường hợp tác vụ sau không cần kết tác vụ chạy gây nên chờ đợi không cần thiết Xét ví dụ sau: giả sử cần thực. .. Top += dY; } base.OnMouseMove(e); } } Biên dịch chương trình, MyDragDrop xuất ToolBox, kéo vào form sau: Chạy chương trình để thấy thành Tạo sử dụng Dialog Khi sử dụng Dialog chủ yếu nhằm