Events ( Các sự kiện)
Khi một ứng dụng chạy trên nền window nhận các thông báo khi môt việc gì đó xảy ra ta
gọi đó là event . ví dụ khi ta nhấn nút chuột thì cửa sổ ứng dụng mà ta nhấn sẽ được
thông báo event này .
Trong C# , các event thực sự là 1 dạng đặc biệt của delegate
Tuy nhiên ta không cần quan tâm các delegate bên dưới làm gì . ta sẽ tiếp cận event trên
quan điểm phần mềm client ( client software). ta sẽ tập trung vào phần mã cần viết để
nhận các thông báo của event ,không c
ần quan tâm bên dưới làm gì .
các event trong C# hơi giống với các khái niệm của VB mặc dù cú pháp và các thực thi
bên dưới thì không giống.
Consumer's view of event ( Cách nhìn của consumer về các event)
Consumer ở đây là các ứng dụng mà muốn được thông báo khi điều gì đó xảy ra.cũng có
thể là các phần mềm khác( thường là hệ điều hành window hay .NET Framework ). ta
xem các phần mềm khác này là bộ phát sinh event ( event generator)
Ở nơi nào đó trong consumer sẽ có 1 phương thức mà sẽ được gọi khi 1 MouseClick xaỷ
ra.phươ
ng thức này đưọc gọi là event handler ( bộ xử lí event) cho event và 1 tên hay hơn
cho nó trong trường hợp này là OnClick() .để gửi đến bộ phát sinh event thông tin này
,consumer sẽ phải đặt 1 tham chiếu đến OnClick() vào bên trong 1 delegate. mỗi lần bộ
phát sinh event có thông tin này ,khi thăm dò thấy chuột được nhấn, nó dùng delegate này
để gọi phương thức Onclick() của Consumer.
Tất cả các bộ xử lí event phải trông giống như sau:
void OnClick(object sender, EventArgs e) // e cũng có thể được dẫn xuất từ EventArgs
{
// mã xửlí event
}
Bộ xử lí event chỉ có thể trả về void.tất cả những gì bộ phát sinh event muốn làm là gọi 1
phương thức. không cần biết consumer làm gì để đáp ứng.các bộ xử lí phải lấy 2 thông
số. thông số đầu là 1 tham chiếu đến đối tượng sinh ra event. thông số thứ 2 phải là 1
tham chiếu đến lớp cơ sở .NET , System.EvetnArgs ,hoặc là 1 lớp dẫn xuất từ nó.1 lớp
dẫn xuất có thể chứ
a thông tin thêm về event như vị trí chuột ,phím nào được nhấn trên
bàn phím.
Để báo cho bộ phát sinh event biết ta quan tâm đến event. ta cần bộ phát sinh event tạo 1
vài mục mà có thể nhận các yêu cầu cho thông báo. mục này sẽ là thành viên public của
lớp .NET mà đại diện cho bộ phát sinh event,và là thành viên của kiểu event . thành viên
này là 1 dạng đặc biệt của multicast delegate.ta giả sử rằng consumer có 1 tham chiếu đến
bộ phát sinh event, qua trung gian là 1 biến tham chiếu gọi là Generator, và đó là thành
viên mà đại diện cho event được gọi là MouseClick. trong lớp consumer ,ta viết :
EventGenerator Generator = GetAReferenceToTheEventGeneratorSomehow();
Ta giả sử rằng EventGenerator đại diện cho lớp bộ phát sinh event
consumer có thể thông báo với bộ phát sinh event rằng nó muốn nhận thông báo về việc
nhấn chuột bằng dòng mã sau :
Generator là 1 tham chiếu đến bộ phát sinh event
Generator.MouseClick += OnClick();
Khi bộ phát sinh event không quan tâm đến event này nữa ta có thể thông báo cho bộ
phát sinh event như sau :
Generator.MouseClick -= OnClick();
Consumer của ta có thể yêu cầu các thông báo của nhiều event mà nó muốn, thậm chí là
yêu cầu các thông báo từ các nguồn khác.vì vậy mà thông số đầu tiên được truyền đến bộ
xử lí event là tham chiếu sender .ví dụ như trong một ứng dụng win form ta có nhiều nút
nhấn , tất cả các nút đều có thể thông báo khi nó bị nhấn , bằng cách kiểm tra tham chiếu
sender ta có thể biết được nút nào được nhấn. ngoài ra các consumer khác nhau có thể
yêu cầu được thông báo các event giống nhau.đơn giản chỉ vi
ệc cộng bộ xử lí event của
nó vào các event bởi vì theo cách các multicast delegate làm việc , thì khi event được bắt,
tất cả các bộ xử lí event sẽ được kích.
Ví dụ về các Event : Console Notifications
Ta sẽ xem xét ví dụ UserInputNotify
.trong ví dụ này ta xét lại công ty điện thọai
Mortimer Phones .
ta sẽ viết 1 đoạn chương trình console nhỏ trình bày 1 thông điệp đến người dùng. trong
ví dụ người dùng sẽ chọn lựa giữa việc xem 1 thông điệp riêng từ Mortimer hay 1 quảng
cáo chung. chương trình sẽ tiếp tục hỏi người dùng thông điệp muốn thấy cho đến khi
nguời dùng gõ X và nhấn Enter để thoát chương trình. tuy nhiên ta sẽ sắp xếp lại cấu trúc
chương trình
để dùng các event.
Có 2 đối tượng cần quan tâm trong mã của ta :
UserInputMonitor -đây là đối tượng mà tiếp xúc với người dùng . nó chịu trách nhiệm hỏi
nguời dùng thông điệp họ muốn thấy.
MessageDisplayer - đây là đối tượng mà chịu trách nhiệm cho việc trình bày thông điệp
tương ứng.
Bởi vì phần trình bày thông điệp không liên quan đến việc người dùng nhập , nên nó
không biết khi nào sẽ trình bày thông điệp hay thông điệp nào sẽ được trình bày - do đó
nó cần nhờ đến UserInputMonitor . UserInputMonitor sẽ làm điều này bằng cách đưa ra 1
event. MessageDisplayer sẽ thông báo UserInputMonitor mà nó muốn được bảo khi
người dùng yêu cầu trình bày thông điệp .sau đó khi người dùng tạo ra một yêu cầu
,UserInputMonitor sẽ đưa ra 1 event tương ứng, làm cho MessageDisplayer được gọi
trong bộ xử lí event.
1 lớp khác ManagersStaffMonitor, mà sẽ bảo Motimer khi ai đó yêu cầu để xem thông
điệp riêng của anh ấy. cho mục đích này ManagersStaffMonitorsẽ trình bày 1 hộp thoại
nói Kaark! .ta thấy kiến trúc event thích hợp làm điều này bởi vì tất cả những gì ta phải
làm là để Mortimer biết về event đưọc đặt trong bộ xử lí event riêng của
ManagersStaffMonitor vào trong event . khi 1 khách hàng yêu cầu xem thông điệp thì
event sẽ gọi cả hai bộ xử lí event liên tiếp nhau . nếu 1 phần mềm khác muốn được thông
báo event này , nó s
ẽ làm cùng việc sau : viết 1 bộ xử lí event và thêm nó đến event trong
UserInputMonitor. theo cách này , bộ xử lí event có thể được nối với nhau.
cấu trúc của chương trình ta sẽ như sau :
Trong biểu đồ trên ,ta đánh dấu trong bộ xử lí event cho lớp consumer, MessageDisplayer
và ManagersStaffMonitor, cũng như event trong lớp bộ phát sinh event của ta
UserInputMonitor. để truyền chi tiết của event ,ta sẽ dùng 1 lớp mới
,UserRequestEventArgs, mà ta sẽ dẫn xuất từ System.EventArgs , mà sẽ thi hành 1 thuộc
tính,Request .cho biết người dùng chọn đề ngh
ị nào.
Đầu tiên ta viết lớp UserRequestEventArgs ,ta sẽ dùng 1 bảng liệt kê để liệt kê các event
có thể có :
enum RequestType {AdRequest, PersonalMessageRequest};
class UserRequestEventArgs : EventArgs
{
private RequestType request;
public UserRequestEventArgs(RequestType request)
: base()
{
this.request = request;
}
public RequestType Request
{
get
{
return request;
}
}
}
Ta đơn giản thêm 1 trường và thuộc tính chỉ định người dùng chọn đề nghị nào.
tiếp theo ta xây dựng bộ phát sinh event ,UserInputMonitor:
class UserInputMonitor
{
public delegate void UserRequest(object sender, UserRequestEventArgs e);
public event UserRequest OnUserRequest;
public void Run()
{
bool finished = false;
do
{
Console.WriteLine("Select preferred option:");
Console.WriteLine(" Request advertisement - hit A then Return");
Console.WriteLine(" Request personal message from Mortimer " +
"- hit P then Return");
Console.WriteLine(" Exit - hit X then Return");
string response = Console.ReadLine();
char responseChar = (response == "") ? ' ' : char.ToUpper(response[0]);
switch(responseChar)
{
case 'X':
finished = true;
break;
case 'A':
OnUserRequest(this, new
UserRequestEventArgs(RequestType.AdRequest));
break;
case 'P':
OnUserRequest(this, new
UserRequestEventArgs(RequestType.PersonalMessageRequest));
break;
}
}
while (!finished);
}
}
Lớp này chứa chỉ 1 dòng mã mà ta thực sự dùng từ khoá event để khai báo 1 event. 2
dòng mã ta quan tâm là :
public delegate void UserRequest(object sender, UserRequestEventArgs e);
public event UserRequest OnUserRequest;
Đầu tiên ta định nghĩa delegate và sau đó khai báo 1 thể hiện của nó.lưu ý rằng nó phải
trả vế 1 void, và lấy 1 object và 1 thông số dẫn xuất từ EvenArgs .
Dòng thứ 2 ta bảo trình biên dịch rằng lớp này chứa 1 event thành viên, có kiểu là
delegate.
Phần cuối của lớpUserInputMonitor đưa ra phương thức run() , mà tạo vòng lặp yêu cầu
ngưòi dùng nhập.chú ý cách mà event được bắt :
switch(responseChar)
{
case 'X':
finished = true;
break;
case 'A':
OnUserRequest(this, new UserRequestEventArgs(RequestType.AdRequest));
break;
case 'P':
OnUserRequest(this, new
UserRequestEventArgs(RequestType.PersonalMessageRequest));
break;
}
Ta đơn giản gọi event mà tự động thực thi bất kì phương thức nào nó tham chiếu . trong
mỗi trường hợp ta khởi tạo 1 lớp dẫn xuất EventArgs mới để truyền đến bộ xử lí event
Tiếp theo là lớp consumer , đầu tiên là MessageDisplayer :
class MessageDisplayer
{
public MessageDisplayer(UserInputMonitor monitor)
{
monitor.OnUserRequest +=
new UserInputMonitor.UserRequest(UserRequestHandler);
}
protected void UserRequestHandler(object sender, UserRequestEventArgs e)
{
switch (e.Request)
{