Về sự kiện

Euler

Administrator
Thành viên BQT
Chào mọi người. Tôi là Admin Euler của diễn đàn THVBA.
Vấn đề sau đây tôi mang tới cho các bạn rất thú vị.
Mọi người đã từng sử dụng SỰ KIỆN (Events) trong VBA chưa?
Sự kiện rất quan trọng, nó biến Excel giống như là một ứng dụng thực thụ.
Trình xử lý sẽ bắt sự kiện như là khi bạn tác động click đúp lên một cells, hoặc khi một sheet nào đó được kích hoạt (active), tự sự kiện nắm bắt được này sẽ tiến hành xử lý theo cách chúng ta mong muốn.
Các sự kiện VBA được viết trên nhiều trang web, vì vậy tôi muốn viết về một sự kiện đặc biệt nhỏ trong topic này.
Trong topic này tôi sẽ trình bày:
・Về WithEvents
・Cácchh sử dụng WithEvents
・Phương pháp tự tạo Events
Cụ thể chúng ta sẽ đi theo mục lục sau:
 

Euler

Administrator
Thành viên BQT
1. Từ khóa WithEvents nghĩa là gì

WithEvents
là từ khóa đặc biệt giống như là nó chụp lấy sự kiện của đối tượng (object) nào đó, có khi thì bắt sự kiện của sheet của file Excel khác (không phải file chứa code).
Phương thức sử dụng từ khóa WithEvents được mô tả như dưới đây.

・Chỉ cần khai báo WithEvents, ta có thể bắt lấy sự kiện của Object đó một cách tự do tự tại.
・Nhất là khi viết sự kiện cho User Form, sử dụng nó thật vô cùng tiện lợi.
Tất nhiên, bạn cần một thủ tục sự kiện.
Tuy nhiên, rất thường hay bị hiểu nhầm, các thủ tục sự kiện không có trong file Excel đang được xử lý, mà nằm trong file đang thực hiện xử lý (file chứa code).
Hơi khó hiểu phải không? Sau đây tôi sẽ thực hiện mô tả.

2. Bắt sự kiện BeforeClose
Hãy cho đoạn code dưới đây vào thisworkbook :
ThisWorkbook:
Private WithEvents myNewBook As Workbook

Public Property Set NewBook(ByVal myBook As Workbook)
    Set myNewBook = myBook
End Property

Private Sub myNewBook_BeforeClose(Cancel As Boolean)
    MsgBox myNewBook.Name & " : Close file", vbInformation
    Set myNewBook = Nothing
End Sub
Tiếp theo hãy Add Module và tại đó viết code như sau:
Module1:
Sub NewBookHdlSample1()
    Set ThisWorkbook.NewBook = Workbooks.Add
End Sub
Hãy thực thi thủ tục NewBookHdlSample1 .
Nó sẽ tạo ra một file mới (tôi gọi là file X).
Bây giờ bạn hãy đóng file X đi. Sẽ có một thông báo hiện ra.

Mỗi lần bạn thực thi thủ tục NewBookHdlSample1, biến số myNewBook của Module ThisWorkbook sẽ được làm mới.
Do đó, sẽ không thể bắt sự kiện BeforeClose nếu file này (file chứa code) là file cuối cùng được close.
Khi bắt sự kiện một file mới được tạo ra, thì xử lý của nó cũng tương tự như class module.
(Còn nữa)
 

vbano1

SMod
Thành viên BQT
Như vậy bài viết trên đã cho thấy, file viết code (file 1) đã bắt sự kiện của một file khác (không chứa code VBA).
Bạn cần đăng nhập để thấy đính kèm

Bây giờ nếu như ta muốn bắt sự kiện toàn bộ các Book mới được tạo ra. Ta nghĩ tới .
Tôi tạo class mới tên là clsHdlBook. Trên đó viết code như sau :
clsHdlBook:
Private WithEvents myNewBook As Workbook

Public Property Set NewBook(ByVal myBook As Workbook)
    Set myNewBook = myBook
End Property

Private Sub myNewBook_BeforeClose(Cancel As Boolean)
    MsgBox myNewBook.Name & " dang duoc close", vbInformation
    Set myNewBook = Nothing
End Sub
Trên Module tôi viết code như sau:
Module2:
Sub NewBookHdlSample2()
    'Thu thap su kien cho vao collection
   
    'Bien Static co tac dung trong khi ma Book van dang open
    Static myClsCol As New Collection
   
    'Khi phat sinh su kien moi thi se cat vao mot bien tam thoi
    Dim myHdlBook As clsHdlBook
   
   
    'Khi phat sinh su kien moi thi cat no vao collection
    Set myHdlBook = New clsHdlBook
   
    'Khi them Book moi, thi thiet dinh thuoc tinh cho NewBook
    Set myHdlBook.NewBook = Workbooks.Add
   
    'Them vao collection
    myClsCol.Add myHdlBook
    'Giai phong bo nho
    Set myHdlBook = Nothing
End Sub
Bạn hãy chạy thủ tục NewBookHdlSample2 hai, ba lần.
Bạn cần đăng nhập để thấy đính kèm

Sau đó lần lượt close các file mới vừa được tạo ra. Ta thấy rằng sự kiện close file của các file này đã được file VBA bắt chính xác.
 

tuhocvba

Administrator
Thành viên BQT
3. Tự tạo sự kiện bằng VBA
Sự kiện trong Excel hay Access ta có thể tự tạo thông qua vòng lặp vô hạn.
Quá trình xử lý bên trong vòng lặp kiểm tra trạng thái của một thứ gì đó và thời gian khi sự thay đổi xảy ra được coi là thời điểm sự kiện xảy ra.
Tuy nhiên, nếu quá trình này chiếm quyền kiểm soát, ta sẽ không thể thực hiện các hành động khác.
Đó là một vấn đề, vì vậy tôi sẽ sử dụng hàm "DoEvents".
Bây giờ bạn hãy tạo workbook mới và dán đoạn code dưới đây.
Mã:
Sub ShtsIncreaseHandler()
    Dim myWkShtsCnt As Long
    Dim myGpShtsCnt As Long
    With ThisWorkbook
        myWkShtsCnt = .Worksheets.Count
        myGpShtsCnt = .Charts.Count
        Do
            If myWkShtsCnt < .Worksheets.Count Then
                MsgBox "workbook co so luong sheet la " & .Worksheets.Count - myWkShtsCnt _
                    & "da duoc tang len", vbInformation
            End If
            If myGpShtsCnt < .Charts.Count Then
                MsgBox "Grap sheet " & .Charts.Count - myGpShtsCnt _
                    & " da duoc tang len", vbInformation
            End If
            myWkShtsCnt = .Worksheets.Count
            myGpShtsCnt = .Charts.Count
            DoEvents
        Loop
    End With
End Sub
Bây giờ hãy chạy thủ tục trên. Thủ tục này sẽ hoạt động cho tới khi nào bạn đóng file Excel này lại.
Giờ thì mỗi lần bạn thực hiện chèn thêm sheet mới hoặc copy sheet mới, sẽ có thông báo hiện ra.
Đoạn mã trên không chính xác để nói nó đã tạo ra một sự kiện, nhưng đó là ý tưởng cơ bản đằng sau việc bắt một sự kiện, vì vậy hãy hiểu nó.
Bạn cần đăng nhập để thấy hình ảnh
 

vbano1

SMod
Thành viên BQT
Như vậy để giải quyết vấn đề chiếm quyền kiểm soát, người dùng không thể thao tác trên bảng tính Excel khi thủ tục đang chạy với vòng lặp vô hạn Do~Loop thì chúng ta sử dụng lệnh DoEvents.
Bạn cần đăng nhập để thấy đính kèm
 
B

bvtvba

Guest
4. Về câu lệnh Event, RaiseEvent
Bắt chước phần trước sử dụng vòng lặp vô hạn, tôi nghĩ mình muốn tạo ra một sự kiện của riêng mình.
Event có thể sử dụng trong Object Module nhưng ngoài Class Module, nếu viết ở các Module khác thì rất khó để viết sự kiện.
Thông qua lệnh RaiseEvent ta có thể bắt được sự kiện.

Nào, ta sẽ thực hành. Hãy tạo một Class Module có tên là clsBook .
Bạn cần đăng nhập để thấy đính kèm

Viết vào đó code dưới đây:
clsBook:
Option Explicit

Public Event SheetsIncrease(ByVal WkShtsCnt As Long _
    , ByVal GpShtsCnt As Long)

Private myBook As Workbook

Public Property Get Book() As Workbook
    Set Book = myBook
End Property

Public Property Set Book(ByVal myNewBook As Workbook)
    Set myBook = myNewBook
End Property

Public Sub WatchStart()
    Dim myWkShtsCnt As Long
    Dim myGpShtsCnt As Long
    If myBook Is Nothing Then Exit Sub
    With myBook
        myWkShtsCnt = .Worksheets.Count
        myGpShtsCnt = .Charts.Count
        Do
            If myWkShtsCnt < .Worksheets.Count _
                Or myGpShtsCnt < .Charts.Count Then
                RaiseEvent SheetsIncrease( _
                    .Worksheets.Count - myWkShtsCnt _
                    , .Charts.Count - myGpShtsCnt)
            End If
            myWkShtsCnt = .Worksheets.Count
            myGpShtsCnt = .Charts.Count
            DoEvents
        Loop
    End With
End Sub

Private Sub Class_Terminate()
    Set myBook = Nothing
End Sub
Sự kiện SheetsIncrease sẽ phát sinh khi só lượng sheet được gia tăng.
Khi một file Excel được mở thì nó sẽ bắt đầu tham chiếu tới thuộc tính của file này.
Tiếp theo ta sẽ viết code dưới đây vào Module Thisworkbook.
Bạn cần đăng nhập để thấy đính kèm

Mã:
Private WithEvents myBookClass As clsBook

Private Sub myBookClass_SheetsIncrease( _
    ByVal WkShtsCnt As Long, ByVal GpShtsCnt As Long)
    If WkShtsCnt > 0 Then
        MsgBox "So luong sheet " & WkShtsCnt & " da duoc tang len.", vbInformation
    End If
    If GpShtsCnt > 0 Then
        MsgBox "So luong graph sheet " & GpShtsCnt & " da duoc tang len.", vbInformation
    End If
End Sub

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    Set myBookClass = Nothing
End Sub

Private Sub Workbook_Open()
    Set myBookClass = New clsBook
    Set myBookClass.Book = Me
    myBookClass.WatchStart
End Sub
Bây gì thì tương tự như ở phần trước. Khi thêm sheet mới thì sẽ có thông báo hiện ra, tức là sự kiện được bắt. Khác là bạn không phải ấn nút chạy thủ tục như trước.
Trên đây tôi đã giải thích các bước tạo sự kiện, nó rất dễ, bạn có thể tự thực hành, tạo sự kiện cho riêng mình.
 
B

bvtvba

Guest
5. Tổng kết
Cảm ơn các bạn đã đọc một chủ đề dài.
Về việc tự tạo Events, quả nhiên vấn đề này là vấn đề khó, chắc là mọi người cũng đã mệt rồi.

Khi bạn có thể tạo sự kiện của riêng mình, bạn có thể làm nhiều việc hơn cùng một lúc.
Hãy nhớ quy trình xác định một sự kiện bằng cách sử dụng các đối tượng của riêng bạn trong lớp của bạn.
Nguồn:
Mã:
https://www.yuu-progra.com/2020/03/29/vba-events/
 
Top