Hỏi về lỗi khi xoá hàng loạt control trên form bằng code.

  • Thread starter Thread starter ptm0412
  • Ngày gửi Ngày gửi
Liên hệ QC

ptm0412

Bad Excel Member
Thành viên BQT
Administrator
Tham gia
4/11/07
Bài viết
14,504
Được thích
37,187
Donate (Momo)
Donate
Giới tính
Nam
Nghề nghiệp
Consultant
Xin hỏi về vấn đề xoá control hàng loạt bằng VBA bị lỗi:

Nguyên là thành viên Kyo muốn tạo dựng lại Game Trúc xanh, nhưng làm trên form, không làm trên sheet. Code điểu khiển lật ô đã có rồi.
Nhưng khi thay level, số lượng control sẽ thay đổi theo level (16, 24, 36), thì làm theo hướng vẽ sẵn 36 Command button và 36 label, đặt tên theo thứ tự.
Khi đổi level sẽ phải dấu bớt hoặc làm hiện ra các control, đồng thời sắp xếp lại các control khác theo thứ tự, để có thể điều khiển.
Ngoài ra sẽ phải viết sẵn code cho 36 command button, mỗi button 1 sub.
Thấy có vẻ phức tạp, tôi bèn hỗ trợ Kyo bằng code vẽ control hàng loạt. Khi thay đổi thì sẽ xoá hết các control cũ và vẽ lại controls mới, đúng số lượng cần thiết, cho nhanh. Đồng thời, hỗ trợ Kyo dùng Class (tạo 1 Class module) để có thể dùng 1 sub duy nhất điều khiển các button (số lượng buttton thay đổi không ảnh hưởng gì nữa).

Xin nói trước là mỗi lần chạy, code sẽ lần lượt làm những việc sau:

- xoá hết control cũ
- căn cứ vào level hiện tại, vẽ 1 số lượng label, và 1 số lượng commandButton
- Gán caption cho các labels bằng các tên hàm Excel ngẫu nhiên ở các vị trí ngẫu nhiên, theo cặp. (Kyo viết hàm ngẫu nhiên)
- Gán caption cho các Button bằng số thứ tự từ 1 đến hết
- Gán các Button vào Class
- Hiện form

Code đã viết xong, chạy cũng được. nhưng sinh ra lỗi như sau mà không lý giải được là lỗi do đâu.
- Mở file lên, chạy lần thứ nhất, bình thường
- Chạy lần 2, lần 3 có thể bình thường, kể cả chọn lại level
- Nhưng đến 1 lúc nào đó, có thể là lần 2 trở đi (lần 1 chắc chắn không lỗi), sẽ bị sinh ra lỗi: Invalid Forward reference, or refer to uncompiled type. Chỉ có thể end, không có debug
- Nếu cố nhấn nút play lần nữa, thì lại ra thông báo khác: Element not found. Lần này thì cho debug.
- Không chạy thêm được lần nào nữa, trừ khi làm như sau:

  • Nhấn debug
  • Nhấn nút stop trên thanh công cụ
  • Mở form bằng nút Run trên thanh công cụ
  • Không làm gì cả, Đóng form lại
  • Quay trở ra sheet
  • Chơi bình thường, 5, 7 lần không thấy lỗi lại. Kể cả thay đổi level trên sheet
- Nếu thay level bằng option button trên form, thì lỗi còn nặng hơn, xuất hiện nhiều hơn. Thậm chí 2 file gần như giống nhau hoàn toàn, cũng sinh lỗi nhiều ít khác nhau.
- Nếu để nguyên Excel, chỉ đóng file mở lên lại, thì xác suất lỗi giảm xuống.

Nhờ mọi người test hộ, tìm nguyên nhân, và tìm hướng khắc phục. Để Kyo tiếp tục hoàn thiện dự án VBA Game đầu tay của mình, và cả tôi cũng có dịp học hỏi.
Xin cám ơn,

Ghi chú:
Khi bị lỗi và ngừng code, thấy rằng code Delete Controls không xoá hết được control trên form, chỉ xoá 1 button duy nhất. Do đó lỗi trong code vẽ lại control. Ngoài ra không phát hiện điều gì khác.
 

File đính kèm

Lần chỉnh sửa cuối:
Sư phụ cho ShowModal = False sẽ thấy lỗi rất rõ
Ngoài ra, nếu là em thì em không làm vậy. Em sẽ dùng code để xây dựng form và các controls từ A đến Z (tức ban đầu không có 1 form nào cả). Khi thoát form là lập tức mọi thứ được xóa sạch, như vậy sẽ dễ khống chế hơn.
 
Upvote 0
Cám ơn ndu, nhưng không hiểu set ShowModal = False rồi làm gì tiếp? Lỗi vẫn xuất hiện và vẫn không biết nguyên nhân

Còn vụ xoá hẳn form làm lại thì mình có nghĩ đến, nhưng theo dự án của Kyo thì sẽ có 1 số control khác nữa như:
- Tên 2 đội chơi
- Điểm số của mỗi đội
- Shape hiển thị đội đang trong lượt chơi
- Đồng hồ đếm ngược
- Số điểm cho câu đố hiện hành
- Một số control khác (có thể bỏ hoặc cho vào form khác như option button chọn level)

Nếu xoá hết làm lại sẽ phải vẽ lại cả những control này nữa (cực cho ku Kyo quá).
 
Upvote 0
Cám ơn ndu, nhưng không hiểu set ShowModal = False rồi làm gì tiếp? Lỗi vẫn xuất hiện và vẫn không biết nguyên nhân
.
Ý em nói là ShowModal = False để lỗi xuất hiện thường xuyên hơn (thay vì ta phải chờ đợi không biết chừng nào lỗi mới hiện ra)
Xem đã xem qua file này. Cảm nghĩ ban đầu của em là: Lỗi chỉ có thể có trong quá trình xóa và thêm controls mà thôi
Vậy để thuận tiện cho việc nghiên cứu, ta bỏ bớt những đoạn code không cần thiết, chỉ chừa lại thế này thôi
PHP:
Public Const dH = 50, dW = 120, dGap = 0
Public Level As Long, LevelRows As Long, LevelCols As Long
PHP:
Sub Play()
  Level = Sheet1.[A2].Value
  LevelRows = IIf(Level = 3, 6, 4)
  LevelCols = IIf(Level = 1, 4, 6)
  CreateBtn LevelRows, LevelCols
End Sub
PHP:
Sub CreateBtn(RowsNum, ColsNum)
  Dim Ctl As Control, i As Long, k As Long, j As Long
  On Error Resume Next
  With ThisWorkbook.VBProject.VBComponents("TrucForm")
    For i = 1 To 36
      .Designer.Controls.Remove ("Cmb" & i)
      .Designer.Controls.Remove ("Lb" & i)
    Next
    On Error GoTo 0
    k = 1
    For i = 1 To RowsNum
      For j = 1 To ColsNum
        With .Designer.Controls.Add("forms.Label.1", "Lb" & k)
          .Left = 20 + (j - 1) * (dW + dGap)
          .Top = 20 + (i - 1) * (dH + dGap)
          .Width = dW: .Height = dH
          .Caption = k
          .BorderStyle = 1
          .TextAlign = 2
          .FontSize = 16
          .BackColor = &HFF00&
          .ForeColor = &HFF&
        End With
        With .Designer.Controls.Add("forms.commandbutton.1", "Cmb" & k)
          .Left = 20 + (j - 1) * (dW + dGap)
          .Top = 20 + (i - 1) * (dH + dGap)
          .Width = dW: .Height = dH
          .Caption = k
          .FontSize = 24
          .Font.Bold = True
          .BackColor = &HFFFF&
          .ForeColor = &HC00000
        End With
        k = k + 1
      Next
    Next
  End With
  FormShow
End Sub
PHP:
Sub FormShow()
 TrucForm.Show
End Sub
Xóa hết code trong UserForm và thí nghiệm ---> Nhiêu đây thôi cũng đã lỗi rồi. Và lỗi đấy là:
- Xóa không hết các controls
- Không add được thêm controls vì trùng tên với các controls chưa xóa
---------------
sư phụ nghiêm cứu thêm xem
 

File đính kèm

Upvote 0
Trong bài 1 mình cũng nói là xoá không hết controls, chỉ xoá được 1 cái duy nhất.
Bây giờ đưa vào code Create(), thậm chí không xoá cái nào.

Kể cả để ngoài như cũ hay đưa vào sub Create như bây giờ, nếu bỏ câu Error Resume next, sẽ thấy lỗi là: "Invalid Argument" trong câu:
.Designer.Controls.Remove ("Cmb" & i)

Vấn đề là: Cũng argument đó, mà khi thì chạy được khi thì không chạy? Nếu Invalid thì không bao giờ chạy mới đúng!?

ndu thử tìm dùm xem có câu lệnh nào khác để xoá control đúng hơn không? Hoặc có phải set property control sắp xoá là cái gì khác cho dễ xoá?

Điên đầu mấy ngày rồi.
 
Upvote 0
Tôi xem qua thấy có nhiều vấn đề về code trong file này.
Có lé không nên dùng:
1. ThisWorkbook.VBProject.VBComponents
2. Designer.Controls
Vì nó can thiệp vào chế độ thiết kế-design time, chỉ máy nào thiết lập chế độ (chọn) "Trust access to Visual Basic Project" trong Macro thì mới chạy được. Phần lớn máy cài đặt Excel ban đầu chế độ này không bật.
3. Việc khởi tạo controls và hủy không hết==>Lỗi.
4. Phương pháp dùng Class module chưa hiệu quả, khởi tạo Class phóng khoáng quá, ví dụ ngay từ đầu đã tạo Public Button() As New CmbClass. Biến đối tượng phải kiểm soát kỹ, tạo bao nhiêu và giải phóng khỏi bộ nhớ bấy nhiêu.

Nếu tác giả đồng ý, tôi sẽ sửa lại theo cách của tôi.
 
Upvote 0
Trong bài 1 mình cũng nói là xoá không hết controls, chỉ xoá được 1 cái duy nhất.
Bây giờ đưa vào code Create(), thậm chí không xoá cái nào.

Kể cả để ngoài như cũ hay đưa vào sub Create như bây giờ, nếu bỏ câu Error Resume next, sẽ thấy lỗi là: "Invalid Argument" trong câu:
.Designer.Controls.Remove ("Cmb" & i)

Vấn đề là: Cũng argument đó, mà khi thì chạy được khi thì không chạy? Nếu Invalid thì không bao giờ chạy mới đúng!?

ndu thử tìm dùm xem có câu lệnh nào khác để xoá control đúng hơn không? Hoặc có phải set property control sắp xoá là cái gì khác cho dễ xoá?

Điên đầu mấy ngày rồi.
Sư phụ thử file em vừa sửa xem còn lỗi không?
Cách của em là không đặt tên cho các control mà đánh dấu vào Tag ---> Duyệt qua toàn bộ controls, cái nào có tag chỉ định thì xóa
 

File đính kèm

Upvote 0
Chào Tuân,

1. Cái Class này mình mới làm lần đầu nên cũng không lường hết hậu quả. Tuy vậy biến Public Button() đã được Erase khi thoát form trong sự kiện Form_Deactivate rồi. Chắc còn biến Object khác chưa xoá hết chăng?
2. Dùng ThisWorkbook.VBProject.VBComponents và Designer.Controls là vì mình không biết cách khác, (mới vọc thôi),

Vậy Tuân xem hộ và thay bằng cách khác. Mình cũng muốn học hỏi thêm.
Nhất là giải thích hộ những câu thông báo lỗi khác nhau trong file bài 1, dù chỉ 1 lỗi giống nhau? Và giải thích hộ cũng lỗi đó mà khi chạy được, khi không?

Kyo chắc cũng sẽ đồng ý thôi, vì cháu nó cũng đang học.
 
Upvote 0
Chào Tuân,

1. Cái Class này mình mới làm lần đầu nên cũng không lường hết hậu quả. Tuy vậy biến Public Button() đã được Erase khi thoát form trong sự kiện Form_Deactivate rồi. Chắc còn biến Object khác chưa xoá hết chăng?
2. Dùng ThisWorkbook.VBProject.VBComponents và Designer.Controls là vì mình không biết cách khác, (mới vọc thôi),

Vậy Tuân xem hộ và thay bằng cách khác. Mình cũng muốn học hỏi thêm.
Nhất là giải thích hộ những câu thông báo lỗi khác nhau trong file bài 1, dù chỉ 1 lỗi giống nhau? Và giải thích hộ cũng lỗi đó mà khi chạy được, khi không?

Kyo chắc cũng sẽ đồng ý thôi, vì cháu nó cũng đang học.

Vâng, vậy em sẽ chỉnh lại về code, thuật toán về Game không thay đổi gì. Em sẽ nghiên cứu và giải thích các lỗi sau.
 
Upvote 0
Sư phụ thử file em vừa sửa xem còn lỗi không?
Cách của em là không đặt tên cho các control mà đánh dấu vào Tag ---> Duyệt qua toàn bộ controls, cái nào có tag chỉ định thì xóa

Code ndu chạy không bị lỗi nữa. Nhưng nếu tạo mới các controls đồng thời đặt tên cho chúng, thì lại lỗi như cũ.
Lý do cần phải đặt tên là để kiểm soát và điều khiển control trong quá trình chơi Game.
 
Upvote 0
Đúng là không nên sử dung
1. ThisWorkbook.VBProject.VBComponents
2. Designer.Controls

đặc biệt 2. Designer.Controls

Đã sửa lại sử dụng trực tiếp Form object: TrucForm,

Và lưu ý khi khi tạo lại button, thì cũng phải gắn lại chúng với class - (xem mấy dòng cuối Creatbtn)


các bạn thử test lại

NHƯNG theo tôi, class ở đây sử dụng cứ sao sao ah

nếu vẫn, muốn giữ nguyên thế thì nên thêm sự kiện đổi màu cho các commandbutton vào class luôn
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Cám ơn "vợ đói" nhiều (hi hi)
Test thử thấy OK. Mình cũng nghĩ rằng lỗi xoá không được mà hiện thông báo "Invalid Argument" có nghĩa là dùng sai câu lệnh, nhưng không biết sửa.
Để về nhà test nhiều lần hơn nữa.

Còn vụ gán Cmb vào Class mình đã làm rồi, trong code Form_Initialize
Việc đổi màu thì mình (thực ra là Kyo) không dùng, mà dùng CmbBtn.Visible để hiện Label ở dưới lên. Đó là code LatHinh trong Module2, và LatHinh cũng đã gán sẵn trong Class như vodoi2x nói. (Sub CB_Click của Class Module). Ngay trong file vodoi cũng đã có thể Play game được rồi.

Cũng xin nói cho rõ: Module2 và các Sub trong đó là của cháu Kyo, 19 tuổi, mới bắt đầu học VBA được 3 tháng.
 
Upvote 0
Phát hiện 1 điều:
Nếu Create Control bằng Form.Object, thì không cần Delete. Khi đóng form, nó tự biến mất. Để xem nó biến mất có ảnh hưởng đến các module Play Game sau này hay không. Vì sẽ mở rộng game:
- Có tính điểm từng đội
- Có đồng hồ đếm ngược
- Xong 1 level, chọn Level khác, chơi tiếp
- Tuỳ chọn lưu trữ điểm hoặc xoá điểm
- Tuỳ chọn thay tên người chơi,
- tuỳ chọn chơi 1 người hoặc 2 người
- Có những câu đố ngẫu nhiên hiện ra (Cái này nếu đóng form này mở form kia là tiêu)
- Có điểm thưởng (dùng Msgbox là được rồi)
- Hiện hình nền (Ý tưởng mới của Kyo)
- ...
 
Lần chỉnh sửa cuối:
Upvote 0
Game "Truc Xanh v1.0"

Em đã sửa lại toàn bộ code. Tạm đặt phiên bản "Truc Xanh v1.0".

Các vấn đề đã sửa:

+ Đặt tên biến và các đối tượng, thủ tục theo English cho thống nhất và dễ nhớ
+ Định dạng code cho đẹp hơn
+ Khai báo Option Explicit ở đầu các Modules, Classes để yêu cầu người lập trình phải khai báo biến khi sử dụng. Đây là phương pháp quản lý biến chặt chẽ và xác định lỗi sau này rõ hơn.
+ Đổi vị trí các thủ tục giữ các modules theo nhóm công việc.
+ Tạo lại Class Module "clsCmd" đảm bảo khởi tạo và giải phóng đối tượng khỏi bộ nhớ chắc chắn. Sử dụng và khai thác đối tượng class thay vì gọi kiểu Controls(Index) trong Userform.

Trong code của class có đoạn dưới đây có thể khó hiểu
Public WithEvents mCmdBtn As MSForms.CommandButton
Private mCtrl As MSForms.Control 'Set it = mCmdBtn for getting properties

Giải thích:
Biến mCmdBtn nhận đối tượng CommandButton, nhưng trong class nó không dùng được một số các thuộc tính (properties) của CommandButton.
Biến mCtrl kiểu Control và trỏ (Set) tới biến mCmdBtn.

Như vậy cả hai biến mCmdBtn và mCtrl đều trỏ vào CommandButton trên Userform nên có thể dùng lẫn nhau. Hai biến này bổ trợ cho nhau, tức là biến mCtrl dùng để sử dụng các properties mà chúng không có trong biến mCmdBtn và ngược lại. Đây là một kiến thức lập trình đối tượng các bạn tạm hiểu vậy để áp dụng trong lập trình Class Module.
 

File đính kèm

Upvote 0
Em nghĩ ra được 1 cách rất đơn giản:
- Vẽ luôn 36 CommandButton, 36 Label và đặt tên luôn cho nó
- Đăt 72 controls này vào góc phải của UserForm
- Tùy theo Level, ta sẽ "kéo" 1 số lượng controls thích hợp, đặt vào vị trí thích hợp
Đơn giản thế thôi, code ngắn hơn rất nhiều (khỏi delete, create gì cả)
--------------
Ngoài ra em rút gọn code rất nhiều, chẳng dùng tí gì vùng tạm trên sheet. Tất cả đều dùng Array để xử lý.
Đặt thêm 3 OptionButton trên form để chuyển Level
Ẹc... Ẹc...
--------------
Còn lại: Đồng hồ, tính điểm gì gì đó sư phụ làm tiếp nhé
 

File đính kèm

Upvote 0
Giải thích:
Biến mCmdBtn nhận đối tượng CommandButton, nhưng trong class nó không dùng được một số các thuộc tính (properties) của CommandButton.
Biến mCtrl kiểu Control và trỏ (Set) tới biến mCmdBtn.

Như vậy cả hai biến mCmdBtn và mCtrl đều trỏ vào CommandButton trên Userform nên có thể dùng lẫn nhau. Hai biến này bổ trợ cho nhau, tức là biến mCtrl dùng để sử dụng các properties mà chúng không có trong biến mCmdBtn và ngược lại. Đây là một kiến thức lập trình đối tượng các bạn tạm hiểu vậy để áp dụng trong lập trình Class Module.

Cái này chắc phải tốn 1 thời gian mới tiêu hoá nổi. Nếu hiểu được cái này thì chắc là có thể hiểu vì sao không delete được control tạo bằng Designer.Controls (sẽ đau đầu đây).
 
Upvote 0
Cái này chắc phải tốn 1 thời gian mới tiêu hoá nổi. Nếu hiểu được cái này thì chắc là có thể hiểu vì sao không delete được control tạo bằng Designer.Controls (sẽ đau đầu đây).
Em nghĩ đơn giản hơn! Nhớ có lần em tạo 1 control, đặt tên cho nó, xong xóa đi (làm bằng tay). Tiếp theo em lại tạo 1 control khác, đặt tên giống như control vừa xóa, nó báo lỗi hoài mà chẳng hiểu tại sao (phải đổi tên khác nó mới chịu)
Em nghĩ lỗi trong file đầu tiên của sư phụ thuộc trường hợp này
 
Upvote 0
Em nghĩ ra được 1 cách rất đơn giản:
- Vẽ luôn 36 CommandButton, 36 Label và đặt tên luôn cho nó
- Đăt 72 controls này vào góc phải của UserForm
- Tùy theo Level, ta sẽ "kéo" 1 số lượng controls thích hợp, đặt vào vị trí thích hợp
Đơn giản thế thôi, code ngắn hơn rất nhiều (khỏi delete, create gì cả)
--------------
Ngoài ra em rút gọn code rất nhiều, chẳng dùng tí gì vùng tạm trên sheet. Tất cả đều dùng Array để xử lý.
Đặt thêm 3 OptionButton trên form để chuyển Level
Ẹc... Ẹc...
--------------
Còn lại: Đồng hồ, tính điểm gì gì đó sư phụ làm tiếp nhé
Cám ơn ndu!
Đồng hồ, tính điểm, ... là phần của ku Kyo, chỉ khi nào bí lão chết tiệt mới chỉ dẫn chút đỉnh thôi. Lão chết tiệt bí thì vô đây làm phiền mọi người tiếp!
 
Upvote 0
Kyo có phải con mẹ Dung không nhỉ?

Đoạn code chú viết chưa rõ chỗ nào thì chat hỏi chú (nick YM/Skype: tuanktcdcn), đặc biệt phần lập trình Class Module. Đây là kỹ thuật lập trình đối tượng hay, nắm được nó sau này ứng dụng để làm ra các chương trình khác lớn hơn nhiều. Hãy chú ý tới việc khởi tạo và giải phóng đối tượng khỏi bộ nhớ, không làm tốt việc này sẽ không bao giờ có được ứng dụng tốt!

Cháu tham khảo loạt bài chú viết về Class module - Kỹ thuật Tạo và Wrap đối tượng
 
Upvote 0
Cái này chắc phải tốn 1 thời gian mới tiêu hoá nổi. Nếu hiểu được cái này thì chắc là có thể hiểu vì sao không delete được control tạo bằng Designer.Controls (sẽ đau đầu đây).

Em chưa bao giờ lập trình can thiệp vào Designer.Controls vì có giải pháp khác thay thế. Lỗi trong trường hợp của anh em xem qua thì là do chưa xó được các controls tạo trong userform.
 
Upvote 0
Web KT

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

Back
Top Bottom