Tìm Kiếm Trong Data Validation Excel

Liên hệ QC

Kiều Mạnh

I don't program, I beat code into submission!!!
Tham gia
9/6/12
Bài viết
5,421
Được thích
4,033
Giới tính
Nam
Mình có viết code sau để tạo và sử dụng Data Validation trong Excel sử dụng tốt ...Tuy nhiên có 2 vấn đề phát sinh mà suy nghĩ hoài chua tìm ra cách xử lý ... Vậy úp bài nhờ các Bạn trợ giúp

1/ Vùng dữ liêu nguon() luôn luôn phát sinh nên mình sử dụng Dic để lấy duy nhất gán vào Data Validation ...nếu dữ liệu nhiều phải kéo xuống tìm mất công quá ... Vậy mình muốn hỏi có cách nào từ Sheet2.[C4] ta có thể gõ ký tự đại diện là nó tìm kiếm ra được hay không ?? .... để gán vào đó

2/ Khi mình sử dụng SendKeys ("%{Down}") để cho Data Validation nó xổ xuống cho dễ nhìn thì trên máy mình mất phím Numlock và có sử dụng Shell để bật nó lại tuy nhiên thấy nó cũng cứ nhảy link tinh à

Vậy nhờ các Bạn xem có cách nào khác xử lý tốt 2 vấn đề trên không giúp mình với
Xin cảm ơn
Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Dim Nguon()
    Nguon = Sheet1.Range(Sheet1.[B3], Sheet1.[B65536].End(3)).Value
    Call Validation(Nguon, Sheet2.Range("C4"))
    ''Call ON_Numlock
End Sub

Public Sub Validation(ByVal dArr As Variant, ByVal Target As Range)
    Dim i As Long
    With CreateObject("scripting.dictionary")
        For i = 1 To UBound(dArr)
            If Not IsEmpty(dArr(i, 1)) Then
                .Item(dArr(i, 1)) = .Count
            End If
        Next
        Target.Validation.Delete
        If .Count Then Target.Validation.Add 3, , , Join(.Keys, ",")
        ''SendKeys ("%{Down}")
    End With
End Sub

Sub ON_Numlock()
    CreateObject("WScript.Shell").SendKeys "{NUMLOCK}", True
End Sub
 

File đính kèm

  • Data Validation.xlsb
    17.3 KB · Đọc: 61
Lần chỉnh sửa cuối:
Mạnh mới nghĩ ra 1 giải pháp đơn giản vầy ... Nhờ các bạn Tham gia thêm
1/ Tại C4 ta gõ chữ m xong Enter ...xong ta chọn Biểu tượng Data nó xổ ra cái cần lọc ...
Tương Tự như vây ... Gõ chữ đ vv...

2/ Còn rắc rối lắm ........ là phải thực hiện tới 3 thao tác mới cho ra kết quả

3/ Chưa xử lý được SendKeys

Mong các bạn tham gia thêm
PHP:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Dim Nguon()
    Nguon = Sheet1.Range(Sheet1.[B3], Sheet1.[B65536].End(3)).Value
    If Target.Address = [C4].Address Then
        Call GetListValidation(Nguon(), Sheet2.Range("C4"))
    End If
End Sub

Public Sub GetListValidation(ByVal Arr As Variant, ByVal Target As Range)
    Dim Dic As Object, dk As String, i As Long
    Set Dic = CreateObject("scripting.dictionary")
    dk = Sheet2.Range("C4").Value
    For i = 1 To UBound(Arr)
        If InStr(1, Arr(i, 1), dk) Then
            If Not IsEmpty(Arr(i, 1)) Then
                Dic.Item(Arr(i, 1)) = Dic.Count
            End If
        End If
    Next
    With Target.Validation
        .Delete
        If Dic.Count Then
            .Add 3, , , Join(Dic.Keys, ",")
            ''SendKeys ("%{Down}") ', True
            .ShowError = False
        End If
    End With
End Sub
Cách này của bạn thì sự kiện trên cell đã xử lý xong rồi. Mà làm đơn giản nữa thì sort nguồn rồi dùng offset để thiết lập vùng validation
 
Upvote 0
Cách này của bạn thì sự kiện trên cell đã xử lý xong rồi. Mà làm đơn giản nữa thì sort nguồn rồi dùng offset để thiết lập vùng validation
Mình mới viết sơ bộ vậy ... còn rắc rối lắm ....Sort Nguon thì không được vì nguon còn liên quan các cột và dòng khác nữa
Mình chưa nghĩ ra làm sao gõ vào c4 xong Enter một cái nó ra luôn nếu có 1 chữ m hay nếu có nhiều chữ m thì nó ra 1 list ....nếu bạn biết chỉ dùm
 
Lần chỉnh sửa cuối:
Upvote 0
Mạnh mới nghĩ ra 1 giải pháp đơn giản vầy ... Nhờ các bạn Tham gia thêm

vọc thử !
source của bác ok roài , chỉnh lại 4 chỗ là chạy đc !
số 444 chưa hoàn chỉnh lắm !

PHP:
Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)         '111
    Dim Nguon()
    Nguon = Sheet1.Range(Sheet1.[B3], Sheet1.[B65536].End(3)).Value
    If Target.Address = [C4].Address Then
 
        If Selection.Address = Target.Address Then Exit Sub '---222
        Target.Select                                       '---333
        Call GetListValidation(Nguon(), Sheet2.Range("C4"))
    End If
End Sub

Public Sub GetListValidation(ByVal Arr As Variant, ByVal Target As Range)
    Dim Dic As Object, dk As String, i As Long
    Set Dic = CreateObject("scripting.dictionary")
    dk = Sheet2.Range("C4").Value
    For i = 1 To UBound(Arr)
        If InStr(1, Arr(i, 1), dk) Then
            If Not IsEmpty(Arr(i, 1)) Then
                Dic.Item(Arr(i, 1)) = Dic.Count
            End If
        End If
    Next
    With Target.Validation
        .Delete
        If Dic.Count Then
            .Add 3, , , Join(Dic.Keys, ",")
            .ShowError = False
            SendKeys ("%{Down}")    '---444
        End If
 
       
 
    End With
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
vọc thử !
source của bác ok roài , chỉnh lại 4 chỗ là chạy đc !

PHP:
Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)         '111
    Dim Nguon()
    Nguon = Sheet1.Range(Sheet1.[B3], Sheet1.[B65536].End(3)).Value
    If Target.Address = [C4].Address Then
 
        If Selection.Address = Target.Address Then Exit Sub '---222
        Target.Select                                       '---333
        Call GetListValidation(Nguon(), Sheet2.Range("C4"))
    End If
End Sub

Public Sub GetListValidation(ByVal Arr As Variant, ByVal Target As Range)
    Dim Dic As Object, dk As String, i As Long
    Set Dic = CreateObject("scripting.dictionary")
    dk = Sheet2.Range("C4").Value
    For i = 1 To UBound(Arr)
        If InStr(1, Arr(i, 1), dk) Then
            If Not IsEmpty(Arr(i, 1)) Then
                Dic.Item(Arr(i, 1)) = Dic.Count
            End If
        End If
    Next
    With Target.Validation
        .Delete
        If Dic.Count Then
            .Add 3, , , Join(Dic.Keys, ",")
            .ShowError = False
        End If
 
         SendKeys ("%{Down}")    '---444
 
    End With
End Sub
Vậy là tam ok .... Manh mới thêm Ucase vào 1 chút ...nếu xóa trắng c4 nó lấy hết

PHP:
Public Sub GetListValidation(ByVal Arr As Variant, ByVal Target As Range)
    Dim Dic As Object, i As Long
    Set Dic = CreateObject("scripting.dictionary")
    For i = 1 To UBound(Arr)
        If InStr(1, UCase(Arr(i, 1)), UCase(Target.Value)) Then
            If Not IsEmpty(Arr(i, 1)) Then
                Dic.Item(Arr(i, 1)) = Dic.Count
            End If
        End If
    Next
    With Target.Validation
        .Delete
        If Dic.Count Then
            .Add 3, , , Join(Dic.Keys, ",")
            .ShowError = False
        End If
        If Dic.Count > 1 Then SendKeys ("%{Down}")
        Target.Select
    End With
    Set Dic = Nothing
End Sub
 

File đính kèm

  • Data Validation2.xlsb
    17.9 KB · Đọc: 44
Lần chỉnh sửa cuối:
Upvote 0
dòng SendKeys ("%{Down}") '---444
mình cho lại vào trong if rồi , để bên ngoài bị lỗi ,
còn chừa lại 1 trường hợp là nếu trong danh sách có 1 ký tự thì ko xổ list ra mà cho vào C4 luôn !
mục 444 bác chỉnh lại , so sánh giá trị Dic sao đó để đc như ý ^^
 
Upvote 0
dòng SendKeys ("%{Down}") '---444
mình cho lại vào trong if rồi , để bên ngoài bị lỗi ,
còn chừa lại 1 trường hợp là nếu trong danh sách có 1 ký tự thì ko xổ list ra mà cho vào C4 luôn !
mục 444 bác chỉnh lại , so sánh giá trị Dic sao đó để đc như ý ^^
đang chinh chỗ có 1 cái chưa được .... Chỉnh dùm mạnh đi :D
Thử gõ c xong chon co xem là thấy .....có vấn đề
 
Upvote 0
nói chung cũng ko rành Dic lắm
chỗ đó mình nghĩ là trong trường hợp Dic.Count=1 , và giá trị ô C4 <> giá trị duy nhất trong Dic
thì lấy giá trị trong Dic gán ra ô C4 , nhưng lấy giá trị duy nhất trong Dic ra sao ta @@
sửa phần xét send key chắc là xong rùi đó !
thôi ngủ phát !
 
Upvote 0
và nếu được thì chỉ cho mình thêm phần Hook để lấy mọi ký tự khi bàn phím được nhấn, không phân biệt nhấn trên ứng dụng nào.
Có một số sự kiện trong sytem mà rất có thể một ứng dụng nào đó quan tâm. Vd. tôi rất muốn system báo cho tôi biết khi có một phím nào được nhấn, chuột được click, move (trong cửa sổ nào đó của ứng dụng của tôi hoặc trong trong bất cứ cửa sổ nào trong system), khi có một cửa sổ nào đó được tạo ra hoặc bị hủy (vd. nếu ta khởi động notepad thì có một cửa sổ được tạo ra có class là "notepad", mà trong nó có cửa sổ con có class là "Edit". Cửa sổ con này là toàn bộvùng trắng mà ta nhìn thấy của trình soạn thảo văn bản)
Nếu ứng dụng có nhu cầu thì "đặt đơn" lên system xin hook cụ thể. Ứng dụng đặt đơn bằng cách gọi hàm SetWindowsHookEx với các tham số. Tham số đầu là để "báo cáo" cho sytem về loại hook cần đặt. Tham số thứ hai là địa chỉ của hàm callback. Nếu ta lập trình trong vd. VB, Delphi v...v thì code của callback có thể nằm ở trong code của ứng dụng hoặc nằm trong thư viện DLL. Về các yêu cầu thì phải đọc trong help về tham số thứ 3 và 4. Khi hàm SetWindowsHookEx trảvề giá trị <> 0 thì có nghĩa là hook đã được cài đặt thành công, và kể từ lúc này cho tới khi "nộp đơn" xin hủy hook (UnhookWindowsHookEx) khi sự kiện liên quan tới hook sảy ra thì system sẽ gọi hàm callback (do có địa chỉ của nó rồi) và truyền vào 3 tham số. Trong tham số wParam vàlParam chứa đựng mọi thông tin về sự kiện. Ứng dụng sẽ lọc ra các thông tin cần thiết để xử lý.

Rất có thể có nhiều khách hàng cùng quan tâm vd. sự kiện liên quan tới bàn phím. Lúc này cáckhách sẽ được sắp xếp theo một hàng - anh nào đặt đơn càng muộn thì càng đứng trước. Khi sảy ra sự kiện thì system sẽ gọi callback của anh đầu tiên - anh đăng ký muộn nhất, tiếp theo gọi callback của anh thứ hai v...v. Nhưng không phải lúc nào cũng thế. Tiếp theo tôi sẽ chỉ ra.

Code của callback có thể xử lý sự kiện hoặc không làm gì nhưng luôn trả về kết quả (vì callback là hàm).
1. Nếu callback trả về giá trị do CallNextHookEx trả về, tức có (vd. hàm callback có tên là LowLevelKeyboardProc)
Mã:
LowLevelKeyboardProc = CallNextHookEx(HookHandle, code, wParam, lParam)
thì system sẽ gọi callback của các khách tiếp theo, và cuối cùng thì gửi thông điệp tới cửa sổ đích - cửa sổ mà ở đó có sự kiện sảy ra.
2. Nếu callback trả về giá trị <> 0 (không có code ở điểm 1) thì system sẽ không gọi callback của những khách ở sau và không gửi thông điệp tới cửa sổ đích. Tức nếu có 10 khách mà khách thứ 1 và 2 đều trả về giá trị do CallNextHookEx trả về thì sytem sẽ gọi callback của khách thứ ba. Nếu khách thứ 3 trả về giá trị <> 0 thì sytem sẽ không gọi callback của khách 4, 5, ..., 10, và cũng không gửi thông điệp tới cửa sổ đích. Tức chuỗi "chăm sóc khách hàng" bị đứt ở khách thứ 3. Đây là ích kỷ. Lộc là của chung, mọi người cùng hưởng. Chỉ trừ khi ta có nhu cầu "chính đáng" thì mới ngắt chuỗi "chăm sóc khách hàng".
3. Nếu khách hàng nào trả về giá trị 0 thì system sẽ không gọi callback của những khách đứng sau nhưng sẽ gửi thông điệp tới cửa sổ đích. Riêng về phần này thì nếu tôi còn nhớ nó đúng với những khách khác. Riêng về khách Unikey (Unikey cũng gọi SetWindowsHookEx.) thì lại không đúng. Tức code của tôi ngắt chuỗi "chăm sóc khách hàng" nhưng callback của Unikey vẫn được gọi. Tôi không tìm hiểu thêm vì sao.

Lý thuyết chỉ có vậy. Nhưng nếu ai đó định dùng cho bài cụ thể ở chủ đề này thì chướng ngại trùng trùng điệp điệp. Thậm chí không phải chỉ do Unikey.

Trước hết ta xét việc gỏ một đoạn văn bản không dấu, tức chỉ a-z, A-Z, dùng bàn phím của Windows chứ không dùng Unikey. Sau khi gõ một loạt ký tự thì ta nhấn phím BACK. Vd. gõ "Anh an com go" và phát hiện gõ sai vậy ta nhấn BACK rồi nhấn "a" để có Anh an com ga". Khi bạn bắt được các phím liên tiếp "A, n, h, , a, n, , c, o, m, , g, o, BACK, a" thì bạn phải biết biến chuỗi 15 ký tự đó thành chuối 13 ký tự "Anh an com ga". Bạn có thể ghi nhớ các phím vào một buffer nhưng ở trên là trường hợp dễ, chỉ việc "vứt đi" 2 phím "o" và BACK. Nhưng nếu tôi thấy sai ở giữa văn bản nên tôi click vào giữa văn bản rồi nhấn một loại BACK hoặc DELETE thì làm sao code biết là tôi đã xóa bao nhiêu ký tự và xóa ở đoạn nào? Kết quả là code có một loại ký tự vàmột loạt BACK - DELETE nhưng làm sao code từ một mớ ký tự đó tạo ra được chuỗi đang hiển thị ở trình soạn thảo văn bản? Như thế thì mớ chuỗi ký tự kia là vô dụng, có cũng như không. Mà nếu tôi nhấn BACK - DELETE là còn thông cảm đấy. Nếu tôi bôi đen một đoạn giữa văn bản rồi nhấn DELETE thì code chỉ bắt được 1 DELETE nhưng thực chất cả một đoạn dài đã bị xóa. Và xóa bao nhiêu, ở đâu thì cũng chịu.

Về Unikay nếu nói thật ngắn gọi, sơ qua thì nó đặt đơn hook bàn phím và chuột. Nó hoạt động trên cơ sở đánh tráo. Cụ thể thế nào? Vd. bạn đang có thiết lập Unicode và kiểu gõ Telex. Khi bạn gõ "a" thì nó ghi nhớ trong buffer của mình. Khi bạn gõ tiếp "a" thì nó phát hiện ra và lúc đó nó sẽ giả nhấn BACK để xóa "a" rồi sau đó nó sẽ gửi ký tự "â" tới trình soạn văn bản. Nếu bạn cũng đăng ký hook thì bạn sẽ nhận được 3 phím: "a", "a", BACK. Code của bạn là tổng quát, làm sao nó biết là phải biến a, a, BACK thành "â"? Ai đó có thể gõ bằng bàn phím Anh "a, a, BACK" (gõ nhầm 2 ký tự "a") để có "a" thôi thì sao? Do "â" được Unikey gửi tới cửa sổ đích bằng thông điệp (PostMessageW + WM_CHAR) mà không phải do được gõ trên bàn phím nên code của bạn không nhận được.

Nói tóm lại chướng ngại trùng trùng điệp điệp để có thể dùng trong chủ đề này. Tôi không nói là không thể. Có thể không cần "sưu tầm" một mớ phím gõ mà chỉ ... chăng? Cái quan trọng nhất là ý tưởng. Nếu có ý tưởng thì nhiều khi lại phải thốt lên: ơ sao lại đơn giản thế.

Vọc hook cũng cần nhớ là do ta không làm chủ được code của Excel (ai mà biết nó có gì, cũng chả can thiệp vào nó được) nên rất có thể khi làm một việc nào đó code của ta sẽ xung đột với code của Excel. Vd. làm một việc gì đó khi bảng tính đang ở chế độ Edit cell - ai đó đang gõ dữ liệu trong cell, thì có thể Excel sẽ chết đột ngột, thậm chí không có thông báo báo tử. Vì thế phải rất thận trọng.

Nhưng nếu muốn vọc chơi thì tôi đính kèm tập tin. Tùy nhu cầu xử lý cái gì, lọc cái gì mà code sẽ khác đi. Hãy khởi động tập tin -> nhấn nút Hook -> cữ gõ chơi một lúc, hoặc mở notepad rồi gõ trong notepad (ở VN chắc gõ bằng Unikey). Kết quả sẽ có trong tập tin Excel. Vd. gõ trong notepad "a, a, BACK" thì trong notepad chỉ còn "â" và trong Excel có 3 kết quả "a, a, BACK".
 

File đính kèm

  • WH_KEYBOARD_LL.xlsm
    29.3 KB · Đọc: 57
Upvote 0
dhn46 xin cảm ơn bác batman1, những giải thích của bác rất cặn kẽ dễ hiểu nhưng ứng dụng được là 1 vấn đề tìm tòi lớn của dhn46.

Bác cho dhn46 hỏi thêm 1 câu nữa: trong file ví dụ của bác bắt được Keyboard trên các ứng dụng, nhưng khi dhn46 thử như sau thì không được:

Tài khoản Window: User (Không phải Admin)
Chạy 1 ứng dụng cài đặt bất kỳ => hiện bảng UAC => Gõ keyboard

Khi xem lại file Excel thì trường hợp này không phát hiện.

Vậy có cách nào để bắt được Keyboard cho trường hợp này không ạ? Nếu được bác có thể cho dhn46 tham khảo file được không bác.

dhn46 xin cảm ơn bác.
 
Upvote 0
Trước hết ta xét việc gỏ một đoạn văn bản không dấu, tức chỉ a-z, A-Z, dùng bàn phím của Windows chứ không dùng Unikey. Sau khi gõ một loạt ký tự thì ta nhấn phím BACK. Vd. gõ "Anh an com go" và phát hiện gõ sai vậy ta nhấn BACK rồi nhấn "a" để có Anh an com ga". Khi bạn bắt được các phím liên tiếp "A, n, h, , a, n, , c, o, m, , g, o, BACK, a" thì bạn phải biết biến chuỗi 15 ký tự đó thành chuối 13 ký tự "Anh an com ga". Bạn có thể ghi nhớ các phím vào một buffer nhưng ở trên là trường hợp dễ, chỉ việc "vứt đi" 2 phím "o" và BACK. Nhưng nếu tôi thấy sai ở giữa văn bản nên tôi click vào giữa văn bản rồi nhấn một loại BACK hoặc DELETE thì làm sao code biết là tôi đã xóa bao nhiêu ký tự và xóa ở đoạn nào? Kết quả là code có một loại ký tự vàmột loạt BACK - DELETE nhưng làm sao code từ một mớ ký tự đó tạo ra được chuỗi đang hiển thị ở trình soạn thảo văn bản? Như thế thì mớ chuỗi ký tự kia là vô dụng, có cũng như không. Mà nếu tôi nhấn BACK - DELETE là còn thông cảm đấy. Nếu tôi bôi đen một đoạn giữa văn bản rồi nhấn DELETE thì code chỉ bắt được 1 DELETE nhưng thực chất cả một đoạn dài đã bị xóa. Và xóa bao nhiêu, ở đâu thì cũng chịu..
em cũng vọc vụ này hổm rày và chết ngắt trong đoạn trên.
chắc vọc sỹ mới chơi nên chả nghĩ được ý tưởng để giải quyết.
hic..... hic....
em tải thử file trên về thì việc hook nếu gõ chữ ngay trên cells của excel thì lệnh không thực hiện, nếu nhấn ra ngoài excel thì lệnh mới chạy. vậy thì khác với code của bác dhn46 rồi bác nhỉ
 
Lần chỉnh sửa cuối:
Upvote 0
Tạo danh sách tìm kiếm nhiều cột và lọc hay tim trong validation mà muốn dùng chính ngay mục nhập tại ô soạn thảo excel là kỹ thuật vô cùng khó! Excel không mở đối tượng để người lập trình can thiệp và vào các thuộc tính, sự kiện vào đối tượng nhập liệu. Hiện nay A-Tools đã hack được vào đối tượng này để bắt sự kiện và tôi đã tạo ra tính năng nhập liệu nâng cao. Đây là kỹ thuật lập trình API với Hook, Subclass ở mức độ rất cao cấp, nó không phải can thiệp thông thường như các chuyên gia lập trình API trong Windows trên thế giới. Chính vì thế kỹ thuật bắt sự kiện vào mục nhập liệu của Excel - không chèn Active Controls trở thành "bí quyết" công nghệ.
Video tôi chia sẻ dưới đây không có ý quảng cáo mà chỉ ra những khó khăn và bất cập nếu lập trình VBA thông thường để tạo danh sách tìm kiếm nhiều cột như các sản phẩm khác của nhiều người, bên cạnh đó tôi cũng chỉ ra 8 đặc tính khó mà tôi đã xử lý được trong chức năng này, nếu bạn nào muốn làm thì coi như đây là ý tưởng thiết kế tham khảo.
(Tôi có thể chia sẻ kiến thức về kỹ thuật lập trình API cũng như về VBA trong các vấn đề ngoài chức năng của A-Tools. Sorry!).
 
Upvote 0
Tạo danh sách tìm kiếm nhiều cột và lọc hay tim trong validation mà muốn dùng chính ngay mục nhập tại ô soạn thảo excel là kỹ thuật vô cùng khó! Excel không mở đối tượng để người lập trình can thiệp và vào các thuộc tính, sự kiện vào đối tượng nhập liệu. Hiện nay A-Tools đã hack được vào đối tượng này để bắt sự kiện và tôi đã tạo ra tính năng nhập liệu nâng cao. Đây là kỹ thuật lập trình API với Hook, Subclass ở mức độ rất cao cấp, nó không phải can thiệp thông thường như các chuyên gia lập trình API trong Windows trên thế giới. Chính vì thế kỹ thuật bắt sự kiện vào mục nhập liệu của Excel - không chèn Active Controls trở thành "bí quyết" công nghệ.
Video tôi chia sẻ dưới đây không có ý quảng cáo mà chỉ ra những khó khăn và bất cập nếu lập trình VBA thông thường để tạo danh sách tìm kiếm nhiều cột như các sản phẩm khác của nhiều người, bên cạnh đó tôi cũng chỉ ra 8 đặc tính khó mà tôi đã xử lý được trong chức năng này, nếu bạn nào muốn làm thì coi như đây là ý tưởng thiết kế tham khảo.
(Tôi có thể chia sẻ kiến thức về kỹ thuật lập trình API cũng như về VBA trong các vấn đề ngoài chức năng của A-Tools. Sorry!).
nếu tính năng này cua A tools là miễn phí vậy thầy có thể rộng lượng chia sẻ được không thầy? đang tò mò tìm hiểu để phát triển vài thứ hỗ trợ cho công việc mà bí thù lù @@
 
Upvote 0
Cám ơn batman1, Anh Tuấn đã chia sẻ những kiến thức và kinh nghiệm làm.. Bạn Mutants Men! Anh Tuấn ở trên đã nói đây là kỹ thuật hook nâng cao và nó nằm trong A Tool nên ko thể chia sẻ đc.
... nên phải tự nghiên cứu thôi!
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn batman1, Anh Tuấn đã chia sẻ những kiến thức và kinh nghiệm làm.. Bạn Mutants Men! Anh Tuấn ở trên đã nói đây là kỹ thuật hook nâng cao và nó nằm trong A Tool nên ko thể chia sẻ đc.
... nên phải tự nghiên cứu thôi!
Tôi thấy vụ hook này cũng có một thành viên làm được mà một thời gian rồi không thấy onl. Tớ đây chắc cũng đi xa chủ đề của anh mạnh rồi.
Hic.... Hic....
Chả thấy ảnh đá động gì tới vụ hook này. Chắc không quan tâm
Hic.... Hic....
 
Upvote 0
Tôi thấy vụ hook này cũng có một thành viên làm được mà một thời gian rồi không thấy onl. Tớ đây chắc cũng đi xa chủ đề của anh mạnh rồi.
Hic.... Hic....
Chả thấy ảnh đá động gì tới vụ hook này. Chắc không quan tâm
Hic.... Hic....

File bạn Batman1 chính là kỹ thuật Hook để bắt sự kiện bàn phím trên VBA, xét về chia sẻ mã nguồn với kỹ thuật Hook bàn phím nó tốt nhất trên GPE ! Trước đây có file gần giống file này nhưng nó bẫy chỉ cho nhập phím số. Như mình đã chia sẻ "Can thiệt vào mục nhập của Excel là kỹ thuật Hook, SubClass cao cấp trong lập trình API... Can thiệt vào đối tượng nhập liệu của Excel là vô cùng khó! ". Bạn cứ tìm trên Google từ khoá "Hook" + API + VBA,... thế giới nói nhiều về việc này để nâng cao hiểu biết về nó, không làm việc này thì làm việc khác. Kỹ thuật xử lý sự kiện nhập liệu trong Excel mình làm trong Add-in A-Tools nếu chuyển thành ngôn ngữ VBA để chạy thì sợ rằng không chạy nổi, vì VBA hỗ trợ SubClass rất kém. A-Tools là miễn phí nhiều chức năng nhưng không phải mã nguồn mở, có những bí mật về công nghệ trong sản phẩm này - Cơ sở thương thiệu có tính tính cạnh tranh nên mình không thể chia sẻ (mình chỉ có thể chia sẻ kinh nghiệp, định hướng xây dựng chức/tính năng), các kỹ thuật khác không liên quan chức năng trong A-Tools mình sẽ chia sẻ như: Công thức Excel, VBA, Delphi, API!
(Bạn tìm bài viết của mình trên GPE sẽ thấy nhiêu ví dụ về API, trong đó có file mã nguồn mở Unicode Menu In Userform khá công phu.)

Tôi khuyên các bạn, nếu kiến thức của chúng ta mới chỉ dừng lại ở VBA (kể cả cao cấp VBA thuần tuý) thì đừng sử dụng kỹ thuật Hook, SubClass vì rất dễ làm hỏng ứng dụng, có những lỗi không tìm được lý do. Chỉ dùng khi bạn có hiểu biết nhất định về nó!
 
Upvote 0
dhn46 xin cảm ơn bác batman1, những giải thích của bác rất cặn kẽ dễ hiểu nhưng ứng dụng được là 1 vấn đề tìm tòi lớn của dhn46.

Bác cho dhn46 hỏi thêm 1 câu nữa: trong file ví dụ của bác bắt được Keyboard trên các ứng dụng, nhưng khi dhn46 thử như sau thì không được:

Tài khoản Window: User (Không phải Admin)
Chạy 1 ứng dụng cài đặt bất kỳ => hiện bảng UAC => Gõ keyboard

Khi xem lại file Excel thì trường hợp này không phát hiện.

Vậy có cách nào để bắt được Keyboard cho trường hợp này không ạ? Nếu được bác có thể cho dhn46 tham khảo file được không bác.

dhn46 xin cảm ơn bác.
Thực ra Excel và lập trình chỉ là đam mê chứ không phải công việc nên tôi không tìm hiểu các hệ điều hành khác ngoài XP Home của tôi. Tất nhiên nhiều cái tôi biết nhưng về bảo mật, thiết lập thì không có system thích hợp để kiểm nghiệm. Chả nhẽ không dùng tới trong công việc mà lại bỏ tiền ra mua Windows 7, 8, 10 và những hđh sẽ có? Vì cài Windows lậu mà bị phát hiện thì bị phạt. Nếu công việc cần tới thì cũng liều đấy nhưng công việc không liên quan gì tới lập trình.

Vài lời mở đầu vậy.

1. Tôi đã thử code trên máy bàn với XP Home + Excel 2010. Chỉ có 1 tài khoản Administrator. Kết quả luôn có trong sheet dù gõ phím khi Excel active hay khi notepad active.

Khi nhận được phản hồi của bạn thì tôi đã thử ở 2 máy:

2. notebook của vợ với Windows 7 Starter + Excel 2007
2a. Máy chỉ có 1 tài khoản Administrator. Kết quả như điểm 1.
2b. Tôi lập thêm tài khoản User. Tôi phải cho phép dùng Macro vì mỗi tài khoản có thiết lập riêng. Khi chạy code thì cũng có kết quả như điểm 1.

3. Hàng xóm có laptop với Windows 10 Home 64-bit + Excel 2007. Cũng chỉ có 1 tài khoản Administrator. Chạy code thì cũng cho kết quả như điểm 1.
Tôi tạo thêm 1 tài khoản local (Administrator là tài khoản Microsoft). Cũng cho phép dùng Macro. Khi chạy code thì cũng có kết quả như điểm 1.

Tóm lại thì tôi chạy trên 3 máy từ system cũ XP tới system mới nhất Windows 10 đều thành công. Vì vậy tôi không hiểu bạn bị lỗi gì.

Bạn chạy không thành công với system nào vậy? Không thành công với tài khoản User hay cả với tài khoản Administrator cũng không thành công?

Nếu bạn nhấn phím mà không có kết quả trên sheet thì chỉ có thể là SetWindowsHookEx không thành công. Bạn thử sau dòng
Mã:
HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, Application.Hinstance, 0)
thì thêm code
Mã:
If HookHandle <> 0 Then
    MsgBox "Thanh cong"
Else
    MsgBox "Khong thanh cong"
End If
rồi xem thông điệp thế nào.

Nhiều hàm API khi gọi mà không thành công thì có thể đọc thông tin về lỗi bằng hàm GetLastError. Nhưng trong trường hợp SetWindowsHook thì không biết system có để lại thông tin lỗi không vì tôi không thấy help đả động tới. Nhưng bạn vẫn có quyền thử:
1. Khai báo
Mã:
Private Declare Function GetLastError Lib "kernel32.dll" () As Long
2. Sau dòng
Mã:
HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, Application.Hinstance, 0)
thì thêm code
Mã:
Debug.Print GetLastError
để xem GetLastError trả về giá trị bao nhiêu.

Khi SetWindowsHook thành công thì rõ ràng system sẽ gọi hàm LowLevelKeyboardProc. Nhưng bạn cũng có thể thêm dòng đầu tiên trong hàm là
Mã:
Debug.Print "hic hic"

Tóm lại thì tôi thử thành công trên 3 máy, cả tài khoản Administrator và tài khoản user. Tôi chịu, không biết thất bại của bạn là do đâu.

Bạn nên để ý là tôi đưa code cho bạn vọc chơi thôi. Không phải để bạn áp dụng vào làm chủ đề này. Tôi không chỉ nói về các chướng ngại trùng trùng điệp điệp mà còn nói rõ là cái hook dạng này + chế độ nhập liệu của Excel là trò chơi bật lửa trên thùng thuốc súng. Tùy cái mà làm thôi. Có những cái nguy hiểm và cực khó như Tuân đã nêu. Bởi nguồn gốc là bạn không làm chủ được code của Excel, bạn chỉ diễn nhờ trên sân người khác. Nếu ứng dụng bạn viết từ A đến Z thì bạn mới làm chủ được.

Những cái cực khó mà cần thiết thì phải có ý tưởng để giải quyết. Khi người ta đã có ý tưởng và có code xử lý thì thường không ai chia sẻ miễn phí với người khác cả. Cần câu cơm mà cho không người khác?
 
Upvote 0
Tài khoản Window: User (Không phải Admin)
Chạy 1 ứng dụng cài đặt bất kỳ => hiện bảng UAC => Gõ keyboard

Khi xem lại file Excel thì trường hợp này không phát hiện
Do không đọc kỹ nên tôi chót viết bài trước.

Nếu nói về nhấn phím trong bảng UAC thì ...

Mọi cái sảy ra hay không đều do system quyết định. Ông system là tối cao mà ổng quyết định không gọi các callback thì phải chịu thôi. Tôi không biết hướng giải quyết. Thậm chí tôi không có system với cái UAC kia để thực nghiệm.
 
Upvote 0
Do không đọc kỹ nên tôi chót viết bài trước.

Nếu nói về nhấn phím trong bảng UAC thì ...

Mọi cái sảy ra hay không đều do system quyết định. Ông system là tối cao mà ổng quyết định không gọi các callback thì phải chịu thôi. Tôi không biết hướng giải quyết. Thậm chí tôi không có system với cái UAC kia để thực nghiệm.
Em đã chỉnh UAC ở mức thấp nhất, và máy tính đã có quyền Admin
nhưng khi sửa theo bài #56 thì khi nhấn phím
ở ngoài excel: dữ liệu được điền vào các ô ở trên cell
trong excel: dữ liệu điền ngay ô chọn chứ không điền như trường hợp trên
em có thử thêm Beep sau dòng: Sheet1.Cells(r, c).Value = Text
thì chỉ nghe âm thanh báo khi ở nhấn phím bên ngoài excel, vậy là trong excel thì không thể gọi LowLevelKeyboardProc chạy được
không biết vụ hook này có đả động đến chủ đề này không. nếu đi xa vấn đề quá thì mong mọi người có thể qua topic này, em hỏi mà chả ai quan tâm trả lời cả
hic..... hic....
 
Upvote 0
Đây là kết quả cuối cùng úp lên tặng cho Bạn nào cần thì cứ vậy mà xài ... code xúc tích ngắn gọn ... đơn giản ....

Và dễ hiểu kèm theo tham số của hàm InStr cho những ai mới bắt đầu VBA tìm hiểu

1/ Tại C4 bạn muốn gõ cái giống gì vào đó thì gõ xong Enter

2/ nếu Xóa trắng C4 thì nó lấy hết

3/ Quá trình xử dụng nếu có lỗi gì phát sinh vui lòng báo lại tại đây

4/ Mô tả Hàm InStr cho những Bạn mới làm quen với VBA ... Mạnh Copy từ Internet

Tên hàm:
InStr

Mô tả:
InStr([start, ]string1, string2[, compare])
Tìm chuỗi string2 trong chuỗi string1, tìm từ vị trí start

Tham số:
start
Vị trí tìm
string1
Chuỗi tìm kiếm
string2
Giá trị cần tìm
compare
Chỉ rõ kiểu dữ liệu để so sánh trong quá trình tìm kiếm

Ghi chú:
Dùng cho tham số compare
vbUseCompareOption = –1
Chế độ tùy chọn, VB sẽ tự động lựa lọai dữ liệu thích hợp
vbBinaryCompare = 0
So sánh nhị phân
vbTextCompare = 1
So sánh chuỗi
vbDatabaseCompare = 2
So sánh dữ liệu

Private Sub Form_Load()
Dim SearchString, SearchChar, MyPos
SearchString = "XXpXXpXXPXXP"' String to search in.
SearchChar = "P"' Search for "P".
' So sanh theo cua text tu vi tri 4
MyPos = InStr(4, SearchString, SearchChar, 1)' Returns 6.
' So sanh theo Binary
MyPos = InStr(1, SearchString, SearchChar, 0)' Returns 9.
' So sanh theo Binary, do mac dinh la 0
MyPos = InStr(SearchString, SearchChar)' Returns 9.
MyPos = InStr(1, SearchString, "W")' Returns 0.
End Sub

PHP:
Private Sub Worksheet_Change(ByVal Target As Range)
    Application.EnableEvents = False
        Dim Nguon()
        Nguon = Sheet1.Range(Sheet1.[B3], Sheet1.[B65536].End(3)).Value
        If Target.Address = [C4].Address Then
            If Target.Count = 1 Then
                Call GetListValidation(Nguon(), Sheet2.Range("C4"))
            End If
        End If
    Application.EnableEvents = True
End Sub

Public Sub GetListValidation(ByVal Arr As Variant, ByVal Target As Range)
    Dim Dic As Object, i As Long
    Set Dic = CreateObject("scripting.dictionary")
    For i = 1 To UBound(Arr)
        If InStr(1, UCase(Arr(i, 1)), UCase(Target.Value), 1) Then
            If Not IsEmpty(Arr(i, 1)) Then
                Dic.Item(Arr(i, 1)) = Dic.Count
            End If
        End If
    Next
    With Target.Validation
        .Delete
        If Dic.Count Then
            .Add 3, , , Join(Dic.keys, ",")
            .ShowError = False
        End If
        If Dic.Count > 1 Then
            SendKeys ("%{Down}"), True
        ElseIf Dic.Count = 1 Then
            Target.Value = Dic.keys()
        End If
        Target.Select
    End With
    Set Dic = Nothing
End Sub
 

File đính kèm

  • List Search_Validation.xlsb
    22.8 KB · Đọc: 70
Lần chỉnh sửa cuối:
Upvote 0
ở ngoài excel: dữ liệu được điền vào các ô ở trên cell
trong excel: dữ liệu điền ngay ô chọn chứ không điền như trường hợp trên
Không hẳn như bạn nói, nhưng chuyện điền vào ô chọn là dễ hiểu.

Bạn nhầm lẫn 2 vấn đề: code nhập vào A1:O1, A2:O2, còn nhập vào ô chọn là do Excel nhập. Vd. bạn không dùng hook gì cả nhưng bạn chọn D9 rồi gõ "a" rồi "a" thì có thấy "aa" trong D không? Rõ ràng là có.

Tại sao tôi nói "không hẳn"? Vì bạn chỉ gõ được 1 ký tự khi có ô chọn.

Tôi đính kèm tập tin để mọi người có thể trong một phạm vi nào đó có thể hiểu được hook làm việc như thế nào. Tôi đính kèm tập tin về hook vì tôi hiểu là dhn46 muốn biết về hook.

Nhưng tôi cũng nói rõ là đó chỉ là ví dụ và muốn làm gì thì phải sửa code cho thích hợp
Tôi cũng viết rõ là trò chơi với cell ở chế độ edit là bật lửa trên thùng thuốc súng. Cũng viết rõ không phải để dùng hook giải quyết vấn đề của chủ đề này. Tóm lại: tôi trả lời câu hỏi cụ thể của dhn46. Thế thôi.

Trong tập tin của tôi có CommandButton nên khi click để cài hook thì nó active và không có cell nào được chọn. Vì thế tôi mới nạp phím vào cells (A1:O1, A2:O2, ...). Nếu khi có hook mà cell nào đó được chọn thì chỉ gõ được 1 ký tự, khi gõ ký tự 2 thì Excel chết đột ngột. Vì khi gõ xong 1 ký tự thì mọi người sẽ nhìn thấy trong cell được chọn có ký tự đầu tiên và trỏ văn bản ở sau nó. Khi gõ ký tự đầu thì cell không ở trạng thái như thế (chưa có ký tự trong cell và chưa có trỏ văn bản - caret). Vì thế khi gõ ký tự 2 thì 99,99% chắc chắn thao tác nhập text vào A2 đã xung đột với code của Excel.

Toàn bộ sẽ như thế này: Giả sử D9 đang activate. Bạn gõ "a" thì system gọi callback -> callback nhập "a" vào A1 -> sau khi system gọi hết các callback thì "tuồn" phím cho cửa sổ đang có input trong system, đó là cửa sổ Excel -> Excel nhận được phím "a" nên nó "đưa" vào D9 và hiển thị caret -> bạn gõ ký tự "a" thứ 2 -> system gọi callback -> callback nhập "a" vào A2. Chính lúc này, khi D9 đang ở chế độ Edit, thì 99,99% chắc chắn là thao tác "nhập "a" vào A2" đã xung đột với code của Excel -> Excel chết đột ngột. Rất có thể thread lúc đó đang ở trong một "critical section".

Tôi đã viết rất rõ là code chỉ để vọc cho biết hook hoạt động như thế nào. Mọi dụng ý dùng nó để thử nghiệm trên cell đang nhập liệu, tương tác vào các đối tượng của Excel thì các bạn tự nghiên cứu. Tôi sẽ không trả lời những câu hỏi về những việc đó.

Nếu ai đó muốn thử nghiệm hook cả khi có cell active, ở chế độ Edit thì thay
Mã:
Sheet1.Cells(r, c).Value = Text
bằng
Mã:
Debug.Print Text
hoặc ghi Text vào tập tin trên đĩa.
 
Upvote 0
Web KT
Back
Top Bottom