Chèn Sendkeys vào vòng lặp While

Liên hệ QC

Thien0872

Thành viên mới
Tham gia
3/12/20
Bài viết
7
Được thích
1
Mọi người cho mình hỏi cách để chèn Sendkeys vào vòng lặp với.

Mình có macro như sau: (xem thêm ảnh hoặc file)

Sub TestSendkeysLoop()
Cells(1, 1).CurrentRegion.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
a = 2
b = 2
Cells(a, b).Select
While Cells(a, b) <> ""
While Cells(a, b) <> ""
SendKeys "{f2}"
SendKeys "{enter}"
a = a + 1
Wend
a = 2
b = b + 1
Wend
End Sub


Khi viết macro thì mình tính là
từ ô B2 sẽ nhấn F2 rồi Enter,
cho đến ô B6 là ô trống thì sẽ chuyển lên ô C2 rồi lại F2 Enter cho đến ô C6,
rồi sẽ này lên ô D2 nhưng ô D2 là ô trắng nên macro sẽ dừng lại.

Nhưng khi chạy macro thì vòng lặp While đếm được 8 giá trị cho đến khi kết thúc
và F2 Enter được chạy 8 lần và macro kết thúc ở ô B10.


Mong nọi người giúp đỡ.

Mình xin cảm ơn ạ.
 

File đính kèm

  • 2022.06.10 test sendkeys vao vong lap.xlsm
    21.2 KB · Đọc: 16
  • 2022.06.10 test sendkeys vao vong lap.png
    2022.06.10 test sendkeys vao vong lap.png
    379.3 KB · Đọc: 12
Mọi người cho mình hỏi cách để chèn Sendkeys vào vòng lặp với.

Mình có macro như sau: (xem thêm ảnh hoặc file)

Sub TestSendkeysLoop()
Cells(1, 1).CurrentRegion.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
a = 2
b = 2
Cells(a, b).Select
While Cells(a, b) <> ""
While Cells(a, b) <> ""
SendKeys "{f2}"
SendKeys "{enter}"
a = a + 1
Wend
a = 2
b = b + 1
Wend
End Sub


Khi viết macro thì mình tính là
từ ô B2 sẽ nhấn F2 rồi Enter,
cho đến ô B6 là ô trống thì sẽ chuyển lên ô C2 rồi lại F2 Enter cho đến ô C6,
rồi sẽ này lên ô D2 nhưng ô D2 là ô trắng nên macro sẽ dừng lại.

Nhưng khi chạy macro thì vòng lặp While đếm được 8 giá trị cho đến khi kết thúc
và F2 Enter được chạy 8 lần và macro kết thúc ở ô B10.


Mong nọi người giúp đỡ.

Mình xin cảm ơn ạ.
Cả đoạn code đó mục đích kết quả như code này phải không?
Mã:
Sub TestSendkeysLoop()
With Cells(1, 1).CurrentRegion
    .Cells.NumberFormat = "General"
    .Value = .Value
End With
End Sub
 
Upvote 0
Cả đoạn code đó mục đích kết quả như code này phải không?
Mã:
Sub TestSendkeysLoop()
With Cells(1, 1).CurrentRegion
    .Cells.NumberFormat = "General"
    .Value = .Value
End With
End Sub
Đúng rồi bạn.

cảm ơn bạn nhiều.

nhân tiện cho mình hỏi là không có cách nào cho sendkeys vào vòng lặp à bạn?
 
Upvote 0
Chỉ là mình tò mò không biết tại sao sendkeys nó không chạy theo thứ tự trong vòng lặp mà lại chạy sau khi vòng lặp kết thúc.
Tò mò là đúng. Vì trong trường hợp này bài #2 giải quyết được. Nhưng ở những bài khác bắt buộc phải dùng SendKeys, While, Do ... thì sao? Giả sử không có cách khác thì sao? Các bài trên GPE được viết không chỉ dành cho người hỏi mà còn cho rất nhiều người âm thầm đọc và học tập.

Trước tiên tôi chỉ ra cái sai rõ nhất của bạn.

Lỗi không phải do bỏ qua tham số thứ 2 của SendKeys. Dù thêm TRUE hay FALSE thì vẫn y nguyên thế thôi. Lỗi không ở chỗ đó. Mỗi người có thể tự kiểm nghiệm là thêm tham số thứ 2 chả giải quyết được gì.

Ở thời điểm chào buổi sáng thì code chọn B2: Cells(a, b).Select. SELECT chỉ được dùng 1 lần duy nhất trước khi vào vòng lặp. Sau đó là Chuỗi 8 (F2 + ENTER), mỗi lần ENTER là tự xuống ô bên dưới. Ở thời điểm b = 2, a = 6 có Cells(a, b) = Cells(6, 2) = B6 = "" (B6 đang được chọn) nên vòng lặp TRONG kết thúc -> a = 2, b = b + 1 = 3. Do Cells(a, b) = Cells(2,3) = C2 <> "" nên vào lại vòng lặp NGOÀI -> thực hiện Enter. Nhưng do không có SELECT nữa nên ô đang chọn vẫn đang là B6. Vì thế khi Enter thì xuống B7, tiếp theo lại Enter nên xuống B8, B9, B10.

Tóm lại ở thời điểm vào cột mới, tức B2, C2, D2, ... phải dùng SELECT để chọn các ô đó. Chọn B2 và do 4 Enter nên xuống các ô B3, B4, B5, B6, tiếp theo chọn C2 thì do 4 Enter nên lại xuống C3, C4, C5, C6.

Vậy phải chuyển Cells(a, b).Select vào sau vòng lặp NGOÀI và trước vòng lặp TRONG

Ta xét code
Mã:
Sub TestSendkeysLoop()
Cells(1, 1).CurrentRegion.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
a = 2
b = 2
While Cells(a, b) <> ""
    Cells(a, b).Select
    While Cells(a, b) <> ""
        SendKeys "{f2}"
        SendKeys "{enter}"
        a = a + 1
    Wend
    a = 2
    b = b + 1
Wend
End Sub

Nếu bây giờ chạy thì thấy C2 được chọn và tiếp theo là 8 (F2 + Enter). Tức xuống các ô C3, C4, ..., C10. Tại sao?

Có vẻ như là 8 (F2 + Enter) không được thực hiện ngay mà dường như chúng được hoãn, và chỉ sau khi SELECT lần cuối cùng (C2) thì chúng mới được thực hiện. Ta có thể đoán là trong vòng lặp thì những SendKeys bị dừng, bị hoãn, không được phục vụ ngay mà chỉ khi mọi thao tác tác động vào sheet, ở trường hợp này là SELECT, đã kết thúc thì chúng mới được thực hiện. Vì thế 8 (F2 + Enter) bị hoãn và chỉ khi SELECT cuối cùng được thực hiện ở C2 thì chúng mới được xử lý.

Để kiểm nghiệm các kết luận ở trên thì ta bắt xử lý ngay lập tức bằng cách dùng DoEvents.

Mã:
Sub TestSendkeysLoop()
Cells(1, 1).CurrentRegion.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
a = 2
b = 2
While Cells(a, b) <> ""
    Cells(a, b).Select
    While Cells(a, b) <> ""
        SendKeys "{f2}"
        SendKeys "{enter}"
        DoEvents
        a = a + 1
    Wend
    a = 2
    b = b + 1
Wend
End Sub
Bây giờ thì code chạy ngon rồi. Nhiều người gọi là chạy mượt.

Kết luận: ngoài việc không SELECT mỗi ô đầu cột thì việc dùng SendKeys trong vòng lặp rất "rắc rối". Đôi khi chúng không được thực thi ngay lặp tức mà "bị hoãn" để rồi sau đó "bùng nổ".
---------------
Tôi không đọc kỹ về SendKeys nên những cái sau có thể không chính xác. Tôi suy đoán dựa trên sự hiểu biết của mình về hệ điều hành Windows, về cơ cấu thông điệp. Nói cho cùng hđh Windows hoạt động dựa trên cơ cấu thông điệp. Vd. khi hđh phát hiện có nhấn chuột hay nhấn phím thì một thông điệp sẽ được gửi tới cửa sổ mà ở đó có sự nhấn phím. Thông điệp sẽ được "đặt" vào hàng đợi.

Thông điệp (message) trrong Windows có thể được gửi trực tiếp tới hàm cửa sổ (vd. dùng SendMessage), hoặc được đặt vào hàng đợi - message queue (vd. dùng PostMessage, do hđh Windows "đặt"). Các ứng dụng sẽ "nhặt" lằn lượt các thông điệp từ hàng đợi và "chuyển" chúng tới các cửa sổ (các hàm cửa sổ - window procedure) mà thông điệp được gửi tới để xử lý. Khi ứng dụng bận thì các thông điệp cứ phải nằm chờ ở hàng đợi - message queue. Trong trường hợp của ta 8 (F2 + Enter) nằm ở message queue và chỉ sau khi SELECT C2 chúng ới được "nhặt" từ hàng đợi để xử lý.
 
Lần chỉnh sửa cuối:
Upvote 0
Tò mò là đúng. Vì trong trường hợp này bài #2 giải quyết được. Nhưng ở những bài khác bắt buộc phải dùng SendKeys, While, Do ... thì sao? Giả sử không có cách khác thì sao? Các bài trên GPE được viết không chỉ dành cho người hỏi mà còn cho rất nhiều người âm thầm đọc và học tập.

Trước tiên tôi chỉ ra cái sai rõ nhất của bạn.

Lỗi không phải do bỏ qua tham số thứ 2 của SendKeys. Dù thêm TRUE hay FALSE thì vẫn y nguyên thế thôi. Lỗi không ở chỗ đó. Mỗi người có thể tự kiểm nghiệm là thêm tham số thứ 2 chả giải quyết được gì.

Ở thời điểm chào buổi sáng thì code chọn B2: Cells(a, b).Select. SELECT chỉ được dùng 1 lần duy nhất trước khi vào vòng lặp. Sau đó là Chuỗi 8 (F2 + ENTER), mỗi lần ENTER là tự xuống ô bên dưới. Ở thời điểm b = 2, a = 6 có Cells(a, b) = Cells(6, 2) = B6 = "" (B6 đang được chọn) nên vòng lặp TRONG kết thúc -> a = 2, b = b + 1 = 3. Do Cells(a, b) = Cells(2,3) = C2 <> "" nên vào lại vòng lặp NGOÀI -> thực hiện Enter. Nhưng do không có SELECT nữa nên ô đang chọn vẫn đang là B6. Vì thế khi Enter thì xuống B7, tiếp theo lại Enter nên xuống B8, B9, B10.

Tóm lại ở thời điểm vào cột mới, tức B2, C2, D2, ... phải dùng SELECT để chọn các ô đó. Chọn B2 và do 4 Enter nên xuống các ô B3, B4, B5, B6, tiếp theo chọn C2 thì do 4 Enter nên lại xuống C3, C4, C5, C6.

Vậy phải chuyển Cells(a, b).Select vào sau vòng lặp NGOÀI và trước vòng lặp TRONG

Ta xét code
Mã:
Sub TestSendkeysLoop()
Cells(1, 1).CurrentRegion.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
a = 2
b = 2
While Cells(a, b) <> ""
    Cells(a, b).Select
    While Cells(a, b) <> ""
        SendKeys "{f2}"
        SendKeys "{enter}"
        a = a + 1
    Wend
    a = 2
    b = b + 1
Wend
End Sub

Nếu bây giờ chạy thì thấy C2 được chọn và tiếp theo là 8 (F2 + Enter). Tức xuống các ô C3, C4, ..., C10. Tại sao?

Có vẻ như là 8 (F2 + Enter) không được thực hiện ngay mà dường như chúng được hoãn, và chỉ sau khi SELECT lần cuối cùng (C2) thì chúng mới được thực hiện. Ta có thể đoán là trong vòng lặp thì những SendKeys bị dừng, bị hoãn, không được phục vụ ngay mà chỉ khi mọi thao tác tác động vào sheet, ở trường hợp này là SELECT, đã kết thúc thì chúng mới được thực hiện. Vì thế 8 (F2 + Enter) bị hoãn và chỉ khi SELECT cuối cùng được thực hiện ở C2 thì chúng mới được xử lý.

Để kiểm nghiệm các kết luận ở trên thì ta bắt xử lý ngay lập tức bằng cách dùng DoEvents.

Mã:
Sub TestSendkeysLoop()
Cells(1, 1).CurrentRegion.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
a = 2
b = 2
While Cells(a, b) <> ""
    Cells(a, b).Select
    While Cells(a, b) <> ""
        SendKeys "{f2}"
        SendKeys "{enter}"
        DoEvents
        a = a + 1
    Wend
    a = 2
    b = b + 1
Wend
End Sub
Bây giờ thì code chạy ngon rồi. Nhiều người gọi là chạy mượt.

Kết luận: ngoài việc không SELECT mỗi ô đầu cột thì việc dùng SendKeys trong vòng lặp rất "rắc rối". Đôi khi chúng không được thực thi ngay lặp tức mà "bị hoãn" để rồi sau đó "bùng nổ".
---------------
Tôi không đọc kỹ về SendKeys nên những cái sau có thể không chính xác. Tôi suy đoán dựa trên sự hiểu biết của mình về hệ điều hành Windows, về cơ cấu thông điệp. Nói cho cùng hđh Windows hoạt động dựa trên cơ cấu thông điệp. Vd. khi hđh phát hiện có nhấn chuột hay nhấn phím thì một thông điệp sẽ được gửi tới cửa sổ mà ở đó có sự nhấn phím. Thông điệp sẽ được "đặt" vào hàng đợi.

Thông điệp (message) trrong Windows có thể được gửi trực tiếp tới hàm cửa sổ (vd. dùng SendMessage), hoặc được đặt vào hàng đợi - message queue (vd. dùng PostMessage, do hđh Windows "đặt"). Các ứng dụng sẽ "nhặt" lằn lượt các thông điệp từ hàng đợi và "chuyển" chúng tới các cửa sổ (các hàm cửa sổ - window procedure) mà thông điệp được gửi tới để xử lý. Khi ứng dụng bận thì các thông điệp cứ phải nằm chờ ở hàng đợi - message queue. Trong trường hợp của ta 8 (F2 + Enter) nằm ở message queue và chỉ sau khi SELECT C2 chúng ới được "nhặt" từ hàng đợi để xử lý.
cảm ơn bạn rất nhiều

macro thứ 2 bạn ghi cho tôi không chạy đúng như ý muốn
nhưng tôi đã học được là có thể dùng Doevents để khiến sendkeys chạy theo thứ tự trong vòng lặp.

điều này thật sự rất hữu ích.

từ giờ tôi sẽ có thể tìm hiểu thêm cách hoạt động của sendkeys và luyện tập
 
Upvote 0
cảm ơn bạn rất nhiều

macro thứ 2 bạn ghi cho tôi không chạy đúng như ý muốn
nhưng tôi đã học được là có thể dùng Doevents để khiến sendkeys chạy theo thứ tự trong vòng lặp.

điều này thật sự rất hữu ích.

từ giờ tôi sẽ có thể tìm hiểu thêm cách hoạt động của sendkeys và luyện tập
Vụ SendKey bạn xem thêm tại bài SendKeys
 
Upvote 0
Web KT

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

Back
Top Bottom