Chuyên đề giải đáp những thắc mắc về code VBA

Liên hệ QC

maytinhvp01

Thành viên thường trực
Tham gia
27/7/13
Bài viết
390
Được thích
179
Mình muốn nhờ giải thich câu lệnh " If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c) "
trong ví du:
Public Function LonNhat(Ran As Range)
Dim max As Double, v As Integer, d As Integer, c As Integer
max = Ran.Cells(1, 1)
For d = 1 To Ran.Rows.Count
For c = 1 To Ran.Columns.Count
If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c)
Next c
Next d
v = Tim(max, Ran)
LonNhat = max
End Function
-------------------------------------------------------
[INFO1]Thông báo:
Vì topic này:
http://www.giaiphapexcel.com/forum/...ải-thích-các-code-đề-nghị-các-bạn-gửi-vào-đây
đã quá dài nên BQT đóng lại.
Nay tôi mở topic mới với cùng chủ đề: GIẢI THÍCH NHỮNG THẮC MẮC VỀ CODE
Các bạn nếu có nhu cầu giải thích code, vui lòng post tại đây nhé
NDU96081631

[/INFO1]
 
Chỉnh sửa lần cuối bởi điều hành viên:
Xin chào các bạn,
Tôi chạy đoạn code sau:

Mã:
Option Explicit

Sub TesDic()

    Const txt As String = "NhanVien 0001"
    Const tmp As String = "NhanVien 0000"
   
    Dim arr(), i As Long, j As Long
    Dim dic As Scripting.Dictionary
    Set dic = New Scripting.Dictionary
    'Kích hoạt microsoft scripting runtime

    dic.Add "NhanVien 0002", 10
    dic.Add "NhanVien 0003", 8
    dic.Add "NhanVien 0004", 6
    dic.Add tmp, 0
   
    If Not dic.Exists(txt) Then dic.Add txt, 100
    dic.Remove (tmp)
    dic(txt) = 120
   
    i = dic.Count
    ReDim arr(1 To i, 1 To 2)
   
    For j = 0 To i - 1
        arr(j + 1, 1) = dic.Keys(j)
        arr(j + 1, 2) = dic.Items(j)
    Next j
   
    Range("E2").Resize(UBound(arr), UBound(arr, 2)) = arr
   
End Sub

Kết quả OK, không có lỗi.
Nhưng khi thay đổi cách khai báo:
Mã:
Dim dic As Scripting.Dictionary
Set dic = New Scripting.Dictionary
Thành:
Mã:
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
Thì code báo lỗi:
"Property let procedure not defined and property get procedure did not return an object (Error 451)"

Nhờ các bạn chỉ giúp nguyên nhân ạ.
 
Lần chỉnh sửa cuối:
Upvote 0
Xin chào các bạn,
Tôi chạy đoạn code sau:

Mã:
Option Explicit

Sub TesDic()

    Const txt As String = "NhanVien 0001"
    Const tmp As String = "NhanVien 0000"
  
    Dim arr(), i As Long, j As Long
    Dim dic As Scripting.Dictionary
    Set dic = New Scripting.Dictionary
    'Kích hoạt microsoft scripting runtime

    dic.Add "NhanVien 0002", 10
    dic.Add "NhanVien 0003", 8
    dic.Add "NhanVien 0004", 6
    dic.Add tmp, 0
  
    If Not dic.Exists(txt) Then dic.Add txt, 100
    dic.Remove (tmp)
    dic(txt) = 120
  
    i = dic.Count
    ReDim arr(1 To i, 1 To 2)
  
    For j = 0 To i - 1
        arr(j + 1, 1) = dic.Keys(j)
        arr(j + 1, 2) = dic.Items(j)
    Next j
  
    Range("E2").Resize(UBound(arr), UBound(arr, 2)) = arr
  
End Sub

Kết quả OK, không có lỗi.
Nhưng khi thay đổi cách khai báo:
Mã:
Dim dic As Scripting.Dictionary
Set dic = New Scripting.Dictionary
Thành:
Mã:
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
Thì code báo lỗi:
"Property let procedure not defined and property get procedure did not return an object (Error 451)"

Nhờ các bạn chỉ giúp nguyên nhân ạ.
Nguyên nhân là khai báo dic muộn (late binding), còn trường hợp trên là khai báo dic sớm (early binding)
 
Upvote 0
Nguyên nhân là khai báo dic muộn (late binding), còn trường hợp trên là khai báo dic sớm (early binding)
Xin chào tam888,
Cảm ơn bạn đã giúp đỡ, vâng OT cũng hiểu sự khác nhau giữa khai báo sớm và khai báo muộn.
Vấn đề khai báo sớm hay khai báo muộn OT nghĩ nó tiện cho việc đỡ khỏi tích vào thư viện "microsoft scripting runtime" không nghĩ rằng nó lại ảnh hưởng đến nhiều vấn đề khác nữa.
Ví dụ như vấn đề OT đang thắc mắc.
 
Upvote 0
Xin chào tam888,
Cảm ơn bạn đã giúp đỡ, vâng OT cũng hiểu sự khác nhau giữa khai báo sớm và khai báo muộn.
Vấn đề khai báo sớm hay khai báo muộn OT nghĩ nó tiện cho việc đỡ khỏi tích vào thư viện "microsoft scripting runtime" không nghĩ rằng nó lại ảnh hưởng đến nhiều vấn đề khác nữa.
Ví dụ như vấn đề OT đang thắc mắc.
Ảnh hưởng này thì sửa được, bằng cách gán .Keys và .Items xuống 2 biến trước, rồi mới khai thác
Nguyên nhân thì chắc là lõi vấn đề trong xử lý khác nhau với khai báo sớm và khai báo muộn của Microsoft - code của họ đóng như hộp đen, nên chỉ có thể suy luận là do
- Khai báo sớm -- thì nó dành cho tạo vùng 1 đối tượng rõ ràng
- Khai báo muộn thì có thể chỉ là vùng ảo, nên khó cấp tiếp cho việc xác lập đối tượng mới Array (.keys, .items - là array)
(tuy thế tất cả là suy luận nên có thể không đúng bản chất)

Ảnh hưởng này thì sửa được, bằng cách gán .Keys và .Items xuống 2 biến trước, rồi mới khai thác
Cái này nên làm với cả 2 trường hợp Khai báo sớm và muộn vì có thể giúp chương trình chạy nhanh hơn, và logic hơn
Vì .Keys, :items --> về bản chất nó là phương thức --> có thể nó phải thực hiện tính toán trong đối tượng dic ==> như vậy mỗi lần gọi thì lại mất thời gian truy xuất.


Vấn đề khai báo sớm hay khai báo muộn OT nghĩ nó tiện cho việc đỡ khỏi tích vào thư viện "microsoft scripting runtime" không nghĩ rằng nó lại ảnh hưởng đến nhiều vấn đề khác nữa.
Còn khác nhau nhiều nữa, vì bản chất cách tạo khác nhau -- không chỉ khắc phục sự lười đâu. tất nhiên cả sự tiện nữa
 
Lần chỉnh sửa cuối:
Upvote 0
...
Nhưng khi thay đổi cách khai báo:
Mã:
Dim dic As Scripting.Dictionary
Set dic = New Scripting.Dictionary
Thành:
Mã:
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
Thì code báo lỗi:
"Property let procedure not defined and property get procedure did not return an object (Error 451)"

Nhờ các bạn chỉ giúp nguyên nhân ạ.
Bạn đã làm quen với VBA lâu ròi, và đã hỏi ở GPE nhiều rồi. Đáng lẽ bạn phải biết rằng "code báo lỗi .... " vẫn chưa đủ diễn tả hết.
Lần sau nhớ thêm chi tiết nó báo lỗi ở dòng nào. Tránh cho người khác phải đoán mò.

Theo code trên thì tôi đoán mò rằng nó lỗi ở dòng này:
arr(j + 1, 1) = dic.Keys(j)

Giải thích:
Vần đề này đã từng được một bạn khác (và tôi) giải thích trong một vài thớt về Dictionary. Hiển nhiên là bạn chưa đọc thớt ấy, hoặc có đọc nhưng chưa hiểu vì không có cơ hội thực tiễn.

Khi kết nối sớm, VBA có đủ tư liệu để đoán một số hàm/thủ tục và dùng dạng mặc định để chấp nhận một số ngữ pháp (qua kỹ thuật wapper hoặc hàm mặc định).
Khi kết nối trễ, VBA không có tư liệu để đoán, và vì vậy các hàm/thủ tục phải được gọi đúng ngữ pháp.

Hàm gọi keys của dictionary gọi theo đúng ngữ pháp là Keys(), và nó trả về một collection.
Ngữ pháp đúng thì phải là
arr(j + 1, 1) = dic.Keys()(j)
Và tương tự như vậy cho hàm Items()

Code trước của bạn không bị lỗi là vì khi kết nối sớm, VBA có đủ tư liệu để đoán hàm này và gọi cái wrapper property (thuộc tính giao diện) để hiểu dic.Keys(j) là cái gì.
 
Upvote 0
Xin chào các bạn,
Tôi chạy đoạn code sau:

Mã:
Option Explicit

Sub TesDic()

    Const txt As String = "NhanVien 0001"
    Const tmp As String = "NhanVien 0000"
  
    Dim arr(), i As Long, j As Long
    Dim dic As Scripting.Dictionary
    Set dic = New Scripting.Dictionary
    'Kích hoạt microsoft scripting runtime

    dic.Add "NhanVien 0002", 10
    dic.Add "NhanVien 0003", 8
    dic.Add "NhanVien 0004", 6
    dic.Add tmp, 0
  
    If Not dic.Exists(txt) Then dic.Add txt, 100
    dic.Remove (tmp)
    dic(txt) = 120
  
    i = dic.Count
    ReDim arr(1 To i, 1 To 2)
  
    For j = 0 To i - 1
        arr(j + 1, 1) = dic.Keys(j)
        arr(j + 1, 2) = dic.Items(j)
    Next j
  
    Range("E2").Resize(UBound(arr), UBound(arr, 2)) = arr
  
End Sub

Kết quả OK, không có lỗi.
Nhưng khi thay đổi cách khai báo:
Mã:
Dim dic As Scripting.Dictionary
Set dic = New Scripting.Dictionary
Thành:
Mã:
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
Thì code báo lỗi:
"Property let procedure not defined and property get procedure did not return an object (Error 451)"

Nhờ các bạn chỉ giúp nguyên nhân ạ.
Trong link sau tôi nói về server COM, kết nối sớm và kết nối trễ, sự khác nhau về bản chất của 2 loại kết nối.


Ở link dưới đây người ta hỏi vấn đề y như của bạn


Bạn đọc các bài viết của tôi trong 2 link sau thì bạn phải hiểu được nguyên nhân, và cách viết phải như thế nào. Và hiểu thêm về Dictionary.


-----------
Sau khi đọc các bài và hiểu thì bạn sẽ theo bài tôi hướng dẫn (bài đầu trong 2 link cuối) và sửa
Mã:
arr(j + 1, 1) = dic.Keys(j)
arr(j + 1, 2) = dic.Items(j)
thành
Mã:
arr(j + 1, 1) = dic.Keys()(j)
arr(j + 1, 2) = dic.Items()(j)
 
Upvote 0
Bạn đã làm quen với VBA lâu ròi, và đã hỏi ở GPE nhiều rồi. Đáng lẽ bạn phải biết rằng "code báo lỗi .... " vẫn chưa đủ diễn tả hết.
Lần sau nhớ thêm chi tiết nó báo lỗi ở dòng nào. Tránh cho người khác phải đoán mò.

Theo code trên thì tôi đoán mò rằng nó lỗi ở dòng này:
arr(j + 1, 1) = dic.Keys(j)

Giải thích:
Vần đề này đã từng được một bạn khác (và tôi) giải thích trong một vài thớt về Dictionary. Hiển nhiên là bạn chưa đọc thớt ấy, hoặc có đọc nhưng chưa hiểu vì không có cơ hội thực tiễn.

Khi kết nối sớm, VBA có đủ tư liệu để đoán một số hàm/thủ tục và dùng dạng mặc định để chấp nhận một số ngữ pháp (qua kỹ thuật wapper hoặc hàm mặc định).
Khi kết nối trễ, VBA không có tư liệu để đoán, và vì vậy các hàm/thủ tục phải được gọi đúng ngữ pháp.

Hàm gọi keys của dictionary gọi theo đúng ngữ pháp là Keys(), và nó trả về một collection.
Ngữ pháp đúng thì phải là
arr(j + 1, 1) = dic.Keys()(j)
Và tương tự như vậy cho hàm Items()

Code trước của bạn không bị lỗi là vì khi kết nối sớm, VBA có đủ tư liệu để đoán hàm này và gọi cái wrapper property (thuộc tính giao diện) để hiểu dic.Keys(j) là cái gì.

Xin chào Bác VetMini
Dạ đúng rồi code lỗi ở dòng:
arr(j + 1, 1) = dic.Keys(j)

Con cảm ơn Bác đã chỉ dẫn ạ, lần này chắc con phải đọc đi đọc lại vài lần những vấn đề con thắc mắc :D
Bài đã được tự động gộp:

Trong link sau tôi nói về server COM, kết nối sớm và kết nối trễ, sự khác nhau về bản chất của 2 loại kết nối.


Ở link dưới đây người ta hỏi vấn đề y như của bạn


Bạn đọc các bài viết của tôi trong 2 link sau thì bạn phải hiểu được nguyên nhân, và cách viết phải như thế nào. Và hiểu thêm về Dictionary.


-----------
Sau khi đọc các bài và hiểu thì bạn sẽ theo bài tôi hướng dẫn (bài đầu trong 2 link cuối) và sửa
Mã:
arr(j + 1, 1) = dic.Keys(j)
arr(j + 1, 2) = dic.Items(j)
thành
Mã:
arr(j + 1, 1) = dic.Keys()(j)
arr(j + 1, 2) = dic.Items()(j)

Xin chào Bác Siwtom,
Con cảm ơn Bác nhiều ạ, thực sự giờ con mới tìm hiểu về Dic và về code T_T
Không biết được bao lâu và sẽ đi đâu ạ.
Con chúc Bác nhiều sức khỏe.
 
Lần chỉnh sửa cuối:
Upvote 0
Mã:
Public Sub Supper_man()
Dim Rng As Range, xCell As Range
Dim xRows As Integer
xTitleId = "Supper_Trinh_ACC"
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address, Type:=8)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
xRows = WorkRng.Rows.Count
For Each Rng In WorkRng.Columns
For i = 1 To xRows - 1
        For j = i + 1 To xRows
            If Rng.Cells(i, 1).Value <> Rng.Cells(j, 1).Value Then
                Exit For
            End If
        Next
        WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1)).Merge
        i = j - 1
    Next
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
nhờ các thầy cô xem giúp em đoạn code trên với ạ. em muốn merge ô lại. nhưng các ô vẫn phải giữ giá trị như ban đầu. (lí do là em để em dùng sumproduct cho nó tính được ạ).
Và có thể giữ nguyên fomat và màu định dạng ban đầu không ạ
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Các bạn giúp mình đoạn VBA này với.

Mình muốn Copy 1 dòng sau đó chèn dòng đã copy vào các dòng phía dưới.
Mình tìm được đoạn code như dưới mà không biết làm tiếp như thế nào. Các bạn xem file đính kèm rồi giúp mình với nha. Thanks.

Mã:
Sub InsertCopiedCells()
    Rows("1:1").Select
    Selection.Copy
    Selection.Insert Shift:=x1Down
    Application.CutCopyMode = False
End Sub
 

File đính kèm

Upvote 0
Các bạn giúp mình đoạn VBA này với.

Mình muốn Copy 1 dòng sau đó chèn dòng đã copy vào các dòng phía dưới.
Mình tìm được đoạn code như dưới mà không biết làm tiếp như thế nào. Các bạn xem file đính kèm rồi giúp mình với nha. Cảm ơn.

Mã:
Sub InsertCopiedCells()
    Rows("1:1").Select
    Selection.Copy
    Selection.Insert Shift:=x1Down
    Application.CutCopyMode = False
End Sub
Ơ mới có copy chưa có paste.
 
Upvote 0
Rows("1:1").Select ' => chọn dòng 1
Selection.Copy ' => Copy dòng đang chọn
Selection.Insert Shift:=x1Down ' => chèn tại dòng đang chọn

Nếu muốn chèn tại dòng 3 thì viết rút gọn như sau:

Rows("1:1").Copy ' => Copy dòng 1
Rows("3:3").Insert Shift:=x1Down ' => chèn tại dòng 3
 
Upvote 0
Rows("1:1").Select ' => chọn dòng 1
Selection.Copy ' => Copy dòng đang chọn
Selection.Insert Shift:=x1Down ' => chèn tại dòng đang chọn

Nếu muốn chèn tại dòng 3 thì viết rút gọn như sau:

Rows("1:1").Copy ' => Copy dòng 1
Rows("3:3").Insert Shift:=x1Down ' => chèn tại dòng 3

Cái đoạn này mình hiểu rùi. Ý mình là mình muốn phát triển đoạn này lên 1 xíu là mình muốn chèn cái đoạn mình copy vào nhiều dòng như sheet "kết quả" trong file của mình ấy.

Kiểu như đoạn code dưới. Nhưng mà đoạn này thì mình viết sai rồi nên bị lỗi
Mã:
Do
    Rows(r.Offset(1, 0), r.Offset(j, 0)).Insert Shift:=x1Down
    Set r = Cells(r.Row + j + 1, 1)
    If r.Offset(1, 0) = "" Then Exit Do
    Loop
216579
Bạn xem giúp mình đoạn này hoặc chỉ mình viết đoạn khác với. Cảm ơn nhiều lắm.
 
Upvote 0
Cái đoạn này mình hiểu rùi. Ý mình là mình muốn phát triển đoạn này lên 1 xíu là mình muốn chèn cái đoạn mình copy vào nhiều dòng như sheet "kết quả" trong file của mình ấy.

Kiểu như đoạn code dưới. Nhưng mà đoạn này thì mình viết sai rồi nên bị lỗi
Mã:
Do
    Rows(r.Offset(1, 0), r.Offset(j, 0)).Insert Shift:=x1Down
    Set r = Cells(r.Row + j + 1, 1)
    If r.Offset(1, 0) = "" Then Exit Do
    Loop
View attachment 216579
Bạn xem giúp mình đoạn này hoặc chỉ mình viết đoạn khác với. Cảm ơn nhiều lắm.
Bạn cho cái file lên nhé.
 
Upvote 0
Web KT

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

Back
Top Bottom