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? (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

HeSanbi

Nam Nhân✨Hiếu Lễ Nghĩa Trí Tín✨
Tham gia
24/2/13
Bài viết
2,774
Được thích
4,385
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
Sau khi trao đổi với một lập trình viên chuyên nghiệp chuyên về API trên mrexcel, thì tôi được chia sẻ phương pháp lấy giá trị ô đang nhập liệu với phương pháp khởi tạo Sự kiện cửa sổ từ dll, với RawInput là API chính để thực hiện được điều này, dll này được lưu trữ trong mã VBA được tạo và load trong chính mã VBA. Phương pháp này khá hay nếu các bạn muốn có thể tham khảo và học thêm. Tuy nhiên khởi tạo một Dll trong mã VBA có một nhược điểm, nếu máy tính đó chặn quá trình load dll từ tiến trình chưa đăng ký. Các bạn có thể kiểm thử xem có gặp vấn đề này không. Có thể máy tính công ty sẽ chặn. Vì quá trình tạo và chạy một DLL chưa kiểm chứng an toàn là rất nguy hiểm. Chỉ sử dụng khi bạn tin tưởng người tạo ra dự án.

DLL này là mã VBA...
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

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
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
Sau khi trao đổi với một lập trình viên chuyên nghiệp chuyên về API trên mrexcel, thì tôi được chia sẻ phương pháp lấy giá trị ô đang nhập liệu với phương pháp khởi tạo Sự kiện cửa sổ từ dll, với RawInput là API chính để thực hiện được điều này, dll này được lưu trữ trong mã VBA được tạo và load trong chính mã VBA. Phương pháp này khá hay nếu các bạn muốn có thể tham khảo và học thêm. Tuy nhiên khởi tạo một Dll trong mã VBA có một nhược điểm, nếu máy tính đó chặn quá trình load dll từ tiến trình chưa đăng ký. Các bạn có thể kiểm thử xem có gặp vấn đề này không. Có thể máy tính công ty sẽ chặn. Vì quá trình tạo và chạy một DLL chưa kiểm chứng an toàn là rất nguy hiểm. Chỉ sử dụng khi bạn tin tưởng người tạo ra dự án.

DLL này là mã VBA 100% nhưng được biên dịch thành DLL trong VBIDE twinBasic, các bạn biết đó twinBasic là một IDE đồng thời cũng được xem là một công nghệ biên dịch VB mới thay thế cho VBIDE lỗi thời, để tạo nên một không gian lập trình mới cho giới lập trình VB. Từ khung công tác cho đến lập trình mã, tuy chưa có bản chính thức nhưng là IDE rất tốt cho lập trình viên VB. Các bạn có thể học thêm IDE này cho con đường lập trình của mình.


Dự án dưới đây cho phép ghi lại phím nhấn khi gõ vào ô Excel, và cũng cho phép chặn phím nhấn kiểu Cancel như trong phương thức KeyUp và KeyDown của TextBox, Combobox, ... Rất hữu dụng cho việc chỉ nhập số, hoặc chỉ cho nhập ký tự Alphabet, hoặc ràng buộc bất kỳ.

Các bạn có thể tải về TwinBasic để phát triển thêm mã và biên dịch thành DLL, sau đó chuyển DLL thành mã base64 và lưu vào mã VBA.
Mã nguồn DLL có trong đường dẫn trong mã dự án. Mã nguồn DLL thực chất cũng chỉ là mã VBA. Trong twinBasic bạn có thể viết mã theo xu thế hiện đại hơn rất nhiều thay vì mã VBA thuần túy.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Giải pháp
nhờ anh chị giúp. đơn giá bị sai đơn giá (cộng đơn giá) đối với trùng: tên nhân viên, tên cửa hàng, mã hàng. Mình muốn giữa đơn giá gốc(không cộng đơn giá). Modul3 ở sheet TH
 

File đính kèm

Upvote 0
Web KT

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

Back
Top Bottom