VBA Nâng cao: Khi ô Excel ở chế độ đang nhập liệu thì VBA có thể chạy và lấy giá trị ô không?

Liên hệ QC

HeSanbi

Nam Nhân✨Hiếu Lễ Nghĩa Trí Tín✨
Tham gia
24/2/13
Bài viết
2,523
Được thích
3,811
Giới tính
Nam
Hôm nay tôi mang đến cho các bạn là một câu hỏi nâng cao về lập trình VBA, tôi biết chắc rằng đa số các bạn lập trình chưa biết về quá trình ô Excel ở chế độ đang nhập liệu thì VBA có thể chạy và lấy giá trị ô không? Và làm sao để biết được Excel đang ở chế độ đang nhập liệu.

Không biết là các bạn có bao giờ tìm Google về vấn đề này chưa, Tôi đoán rằng các bạn cũng như tôi, đã viết mã và làm ứng dụng Excel đóng đột ngột, có phải không? Vì ứng dụng của việc viết mã này rất hữu ích cho việc nhập liệu
nên tôi lập nên chủ đề này để mọi người thảo luận.

Đơn cử như ứng dụng Danh sách nhập liệu trong bộ A-Tools của anh @Nguyễn Duy Tuân, các bạn sẽ thấy quá trình nhập là trực tiếp vào ô, không phải giả lập một Edit Box, sau đó mã sẽ chạy để lọc danh sách và hiện thông tin đã lọc như dưới đây.


Liên kết: https://www.youtube.com/watch?v=KieO0fCWdi8


Bài viết này sẽ hướng dẫn các bạn viết nên ứng dụng tương tự "Danh sách nhập liệu trong bộ A-Tools" trên. Nhưng chỉ cần duy nhất mã VBA.

Hôm nay chúng ta cùng bàn về vấn đề này nhé.

Nếu bạn nào đã làm được những điều trên có thể chia sẻ để mọi người tham khảo.
 
Lần chỉnh sửa cuối:
Giải pháp
Hướng dẫn: Tạo cửa sổ gợi ý danh mục nhập liệu khi gõ vào ô như A-Tools

Các bạn biết đó, khi đang nhập liệu ở ô bắt sự kiện để thực thi điều gì đó thì mã VBA không thể nào làm được gì với các đối tượng Userform chính tại luồng chính (nếu cố gắng tác động sẽ làm sập Excel) vì vậy cần tận dụng các hàm API để tạo cửa sổ ngoài luồng Excel và chạy VBA ngoài luồng chính. Lúc này ta có thể vẽ các control vào cửa sổ mà không bị chặn bởi luồng chính.
Các API tạo cửa sổ như CreateWindowW CreateWindowExW (W hiểu là hỗ trợ mã hóa Unicode). Để tạo Hooking cho cửa sổ bắt buộc chạy VBA ngoài luồng chính. Các lớp control có trong các thư viện như Comctl, msedit, inkedit, ... hãy tận dụng...
Hướng dẫn 1: Bắt sự kiện cửa sổ nhập liệu đang hiện hoạt (Phần 1)

Phần này hướng dẫn các bạn cách tạo và bắt sự kiện cửa sổ nhập liệu, vì Excel có hai cửa sổ nhập liệu, cửa sổ ngay tại ô cần nhập và cửa sổ thanh nhập công thức.

Nếu dùng WinSpy++ để xem tên lớp cửa sổ thì sẽ là Excel< và Excel6


excel_edit_box.png

Các bạn cần tạo sự kiện để khi hai cửa sổ này được trỏ chuột trỏ đến thì sự kiện sẽ được kích hoạt.

Để viết mã tương thích đa nền tảng hãy đặt mã sau vào một module toàn cục:

JavaScript:
#If VBA7 = 0 Then
   Public Enum LongLong:[_]:End Enum
   Public Enum LongPtr:[_]:End Enum
#End If
Public Const PTRNULL As LongPtr = 0


Thủ tục dưới đây sẽ lấy Handle của cửa sổ Excel< và Excel6. với các hàm API win32 tương thích cả Office 2010 trở về trước

JavaScript:
Private Sub OFAProjectWindow(Optional ByVal win As Object, Optional XLDesk As LongPtr, Optional XL7 As LongPtr, Optional XL6 As LongPtr, Optional XLFX As LongPtr)
  Dim a, h As LongPtr, z As LongPtr:
  If Val(Application.Version) > 14 Then
    z = win.hWnd
    XLDesk = FindWindowEx(z, 0, "XLDESK", vbNullString)
    XL7 = FindWindowEx(XLDesk, 0, "EXCEL7", vbNullString)
  Else
    Set a = Application: z = a.hWnd
    XLDesk = FindWindowEx(z, 0, "XLDESK", vbNullString)
    h = FindWindowEx(XLDesk, 0, "EXCEL7", win.caption)
    If h = 0 Then
      h = FindWindowEx(XLDesk, 0, "EXCEL7", win.caption & "  [Read-Only]"):
      If h = 0 Then
        h = FindWindowEx(XLDesk, 0, "EXCEL7", win.caption & "  [Repair]"):
        If h = 0 Then h = FindWindowEx(XLDesk, 0, "EXCEL7", win.caption & "  [Repaired]")
      End If
    End If
    XL7 = h
  End If
  XL6 = FindWindowEx(XLDesk, 0, "EXCEL6", vbNullString)
  XLFX = FindWindowEx(z, 0, "EXCEL<", vbNullString)
End Sub

Khi một cửa sổ Dự án Excel mở, thì sẽ có các Handle cửa sổ khác nhau nên cần dựa vào sự kiện Excel.Application để gán chức năng các cửa sổ vào hàm gọi lại với api SetWinEventHook. Dưới đây là Class Module chứa các sự kiện Excel.Application.

(...................Chờ cập nhật)

***Lưu ý:
  1. Các hàm api win32 trong chủ đề các bạn cần tự cập nhật vào mã để mã có thể thực thi.
  2. Mã VBA nên viết trong Class Module để tiết kiệm bộ nhớ.
 
Lần chỉnh sửa cuối:
Upvote 0
Sau khi viết mã cho dự án, để tiếp tục hướng dẫn, thì các giải thuật tăng lên khá nhiều và rất khó cho các bạn làm theo.
Và tôi không có thời gian để tiếp tục hướng dẫn bài viết.
Vì vậy tôi sẽ sớm chia sẻ ứng dụng cuối và mã nguồn để các bạn đọc hiểu.
 
Upvote 0
Như đã hứa chia sẻ cho các bạn dự án bắt sự kiện chuột nhập liệu. Do các dự án khác của tôi mà tôi chưa thể phát triển dự án này.
Hôm nay tôi chia sẻ cho các bạn một dự án siêu nhập liệu bản dựng tạm thời. Dự án này tận dụng quá trình nhập liệu trực tiếp vào ô để thực hiện nhiều hành động và thao tác khác nhau.


Ví dụ tệp dưới đây là một ví dụ điển hình cơ bản cho việc nhập liệu.
Ví dụ nhập số thập phân, nhập các số có hai số sau dấu chấm thập phân. Chỉ cho nhập số, khi nhập đủ số sẽ tự động chuyển ô nhập liệu.

Dự án sử dụng các API của Excel và API của Window để thực hiện được quá trình bắt sự kiện nhập tại ô. Để lập trình API các bạn cần tìm hiểu nhiều thêm kiến thức cơ bản để có thể làm chủ các dự án.

Trong dự án thay vì sử dụng SetWinEventHook để bắt sự kiện trỏ văn bản được kích hoạt tại ô, API SetTimer đã được thay thế tạm thời. API SetTimer thì không an toàn cho một vài trường hợp VBA đang chạy. SetWinEventHook cũng sẽ không an toàn nếu viết mã không theo nguyên tắc dành riêng cho các API sự kiện.

Tôi cũng đã viết đoạn mã giảm độ không an toàn của SetTimer xuống tỉ lệ thấp nhất và sẻ sớm chia sẻ.
 

File đính kèm

  • FastInput.xlsm
    286.7 KB · Đọc: 37
Lần chỉnh sửa cuối:
Upvote 0
Chủ đề này hay quá, có tính ứng dụng cao.
 
Upvote 0
Hướng dẫn: Tạo cửa sổ gợi ý danh mục nhập liệu khi gõ vào ô như A-Tools

Các bạn biết đó, khi đang nhập liệu ở ô bắt sự kiện để thực thi điều gì đó thì mã VBA không thể nào làm được gì với các đối tượng Userform chính tại luồng chính (nếu cố gắng tác động sẽ làm sập Excel) vì vậy cần tận dụng các hàm API để tạo cửa sổ ngoài luồng Excel và chạy VBA ngoài luồng chính. Lúc này ta có thể vẽ các control vào cửa sổ mà không bị chặn bởi luồng chính.
Các API tạo cửa sổ như CreateWindowW CreateWindowExW (W hiểu là hỗ trợ mã hóa Unicode). Để tạo Hooking cho cửa sổ bắt buộc chạy VBA ngoài luồng chính. Các lớp control có trong các thư viện như Comctl, msedit, inkedit, ... hãy tận dụng chúng để tạo các control cho cửa sổ như ListBox, ComboBox, ListView, TreeView, Button, Label, ....

Các bạn cần xem thêm các API sự kiện cửa sổ, sự kiện chuột bàn phím, ... như SetWinHookEvent, SetWindowHookEx, SetWindowLong, ... các API này không khó để nắm bắt.


Để tạo giao diện đồ họa hiện đại cho các control, các bạn có thể học thêm thư viện đồ họa 2D như GDI/GDI+ để tạo. Nếu có khả năng hơn thì Windows Imaging Component và Direct2D (Hiện đại và hiệu xuất cao).


Để giao tiếp giữa hai luồng VBA, có thể tận dụng đăng ký thông báo cửa sổ với API RegisterWindowMessageA, sau khi đăng ký thì khi tin nhắn luồng VBA này được gửi, các WinProc cửa sổ các hàm sự kiện cửa sổ luồng VBA kia sẽ nhận tin nhắn.


Để lọc dữ liệu danh mục nhanh có các cách như sau:
  1. Sử dụng thư viện DAO, ADODB, tận dụng dòng lệnh SQL để lọc.
  2. Sử dụng các API xử lý tại memory để tối ưu tốc độ và hiệu xuất đọc và ghi.

Tôi sẽ sớm có bài viết hướng dẫn lập trình chạy nhiều phiên VBA hoạt động song song với nhau. Giao tiếp song song, để luồng VBA này bận thì luồng VBA kia chạy hoặc bắt sự kiện (Rất dễ dàng để học). Tạm thời các bạn có thể tìm hiểu Google về các api sau để làm được điều này: CoCreateGuid, CLSIDFromString, GetActiveObject, RegisterActiveObject, SetProp, GetProp, RemoveProp, CoDisconnectObject, RevokeActiveObject.
 
Lần chỉnh sửa cuối:
Upvote 0
Giải pháp
Kỹ thuật này hay à, giúp vượt được giới hạn 1 phân luồng của VBA Excel. Lúc trước tôi cứ phải gọi thêm vbcript bên ngoài để chạy, giả lập như đa luồng :). Giờ Microsoft chuẩn bị bỏ vbscript rồi nên sau này không chạy nó được, đổi sang dùng Powershell thì chưa rành.
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom