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