Gọi Sub trong UserForm

Liên hệ QC

giaiphap

==(^o^)==
Tham gia
12/3/07
Bài viết
5,778
Được thích
6,274
Donate (Momo)
Donate
Giới tính
Nam
Tôi có một ý đồ không giống ai thế này.
Tôi có cái Sub trong UserForm tên TenSub, bây giờ từ Module gọi nó thì dùng lệnh nào vậy? Nếu sub đó từ Module thì gọi nó thế này.
Mã:
Application.Run "Module.TenSub"
Nhưng nó nằm trong UserForm tôi dùng lệnh này nó báo lỗi.
Mã:
Application.Run "UserForm.TenSub"
Có bạn sẽ hỏi tại sao không đặt trong Module cho dễ. Do tôi muốn đặt trong UseForm cho dễ quản lý, vì thực tế có rất nhiều UserForm mà mỗi cái lại có tùy biến riêng (Chỉ tên Sub giống nhau).
Nói thêm nửa là: Tôi muốn tùy biến UserForm chứ chỉ gọi cố định thì dùng lệnh này cũng được.
Mã:
Call UserForm.TenSub
 
Tôi có một ý đồ không giống ai thế này.
Tôi có cái Sub trong UserForm tên TenSub, bây giờ từ Module gọi nó thì dùng lệnh nào vậy? Nếu sub đó từ Module thì gọi nó thế này.
Mã:
Application.Run "Module.TenSub"
Nhưng nó nằm trong UserForm tôi dùng lệnh này nó báo lỗi.
Mã:
Application.Run "UserForm.TenSub"
Có bạn sẽ hỏi tại sao không đặt trong Module cho dễ. Do tôi muốn đặt trong UseForm cho dễ quản lý, vì thực tế có rất nhiều UserForm mà mỗi cái lại có tùy biến riêng (Chỉ tên Sub giống nhau).
Nói thêm nửa là: Tôi muốn tùy biến UserForm chứ chỉ gọi cố định thì dùng lệnh này cũng được.
Mã:
Call UserForm.TenSub
Cái này ngộ nè.
 
Upvote 0
Tôi rất ít dùng UserForm cho nên không chắc lắm.
Chỉ đoán theo lý thuyết thì UserForm có lẽ là một object, tức là cách ứng xử của nó giống như class module.
Nếu đúng vậy thì do Project bắt buộc một UserForm phải được dựng lên rồi mới sử dụng được các thuộc tính, phương thức của nó.
Nếu là tôi thì tôi nghiên cứu cách kết nối trễ, tỷ dụ như CreateObject.

Lưu ý: những người chuyên dùng callback có thể có cách dùng pointer để cưỡng bức kết nối thẳng với hàm (non virtual) của bất cứ module nào.
(hàm virtual không có địa chỉ cố định nên không thể kết nối thẳng)
 
Upvote 0
UserForm là một Lớp đối tượng.
Application.Run thì không thể gọi bất kì thủ tục nào trong một Lớp đối tượng.


Có thể tuy biến theo cách này:
---------------------------------------
PHP:
Private Sub Sub_Test()
  VBA.Load UserForm1
  'UserForm1.Show 0
  Dim F
  For Each F In VBA.UserForms
    Select Case F.Name
    Case "UserForm1"
      Call F.TenSub
    End Select
  Next
End Sub
 
Upvote 0
Thắc mắc một cái là sao lại dùng cái này để gọi hàm
Người ta đã giải thích, hàm tuỳ chọn theo biến string.

Đương nhiên có cách đi vòng là dùng một module chính, Application.Run hàm trong module ấy với tham số là string
Bên trong hàm được gọi kia có Select Case để lượt lại, gọi đúng hàm ở module mong muốn.
 
Upvote 0
UserForm là một Lớp đối tượng.
Application.Run thì không thể gọi bất kì thủ tục nào trong một Lớp đối tượng.


Có thể tuy biến theo cách này:
---------------------------------------
PHP:
Private Sub Sub_Test()
  VBA.Load UserForm1
  'UserForm1.Show 0
  Dim F
  For Each F In VBA.UserForms
    Select Case F.Name
    Case "UserForm1"
      Call F.TenSub
    End Select
  Next
End Sub
Đúng cái tôi cần, thật ra trước khi gọi Sub trên UserForm thì UserForm đang được mở, chính vì vậy tôi sửa Sub lại như sau:
Mã:
Sub Sub_Test(aName As String)
  Dim F
  For Each F In VBA.UserForms
    If F.Name = aName Then
        F.TenSub
        Exit Sub
    End If
  Next
End Sub
Thắc mắc một cái là sao lại dùng cái này để gọi hàm
Tôi đã nói lúc đầu, do tôi muốn tùy biến UserForm, lệnh đó chỉ ví dụ thôi, còn thực tế tôi muốn như thế này.
Mã:
Application.Run  tenFrm & ".TenSub"
Trong đó tenFrm là một biến kiểu chuỗi, là tên UserForm muốn gọi.
Người ta đã giải thích, hàm tuỳ chọn theo biến string.

Đương nhiên có cách đi vòng là dùng một module chính, Application.Run hàm trong module ấy với tham số là string
Bên trong hàm được gọi kia có Select Case để lượt lại, gọi đúng hàm ở module mong muốn.
Bác @VetMini hiểu đúng ý tôi.
 
Upvote 0
Chủ thớt ghi rõ là "Gọi Sub" mà anh. :)

--------
Và chủ thớt là người rất cẩn trọng đã xem xét trước khi hỏi vấn đề gì đó.
Application.Run "Module1.GoiSubCuaForm", TenForm, TenMethod

Sub GoiSubCuaForm(TenForm, TenMethod)
With UserForms(TenForm) ' form phải được mở sẵn, nếu khong thì dùng select case TenForm để set miForm = New UserFormTenForm
Select Case TenMethod ' hơi lủng củng, nhưng tạm thử xem
Case "abc" ' ở đây chỉ thử thôi, không chắc VBA cho kết nối trễ kiểu này
.abc
Case "buggeredIfIKnew"
.buggeredIfIKnew
End Select
End With
End Sub

Tôi chỉ gợi ý thôi. Toi khỏng xài UserForm cho nên ngại lập project để thử
 
Upvote 0
Application.Run tenFrm & ".TenSub"

Có thể bác không biết.
Trong lập trình nâng cao hoặc ra sản phẩm Add-in cho người dùng cuối thì tất cả các Thủ tục không sử dụng cho bảng tính cần được viết "ẩn danh" và gọi với phương thức hàm Application.Run nếu code nằm ngoài module chính.

Ví dụ: Tôi có thủ tục :

Private Function A()
End Function

Viết như vậy thì thủ tục này sẽ không được tham chiếu đến ứng dụng Excel.

Và gọi:
Application.Run "'" & ThisWorkbook.Name & "'!module1.A"

Gọi ngoài tiến trình:
Dim oApp as Object
Set oApp = VBA.GetObject("App FullPath")
oApp.Run "'AppName'!module1.A"

Ưu điểm của Phương thức: Cho phép nhập số đối số động từ 1 đến 32.
Nhược điểm: Code quá dài, không thay đổi giá trị của đối số truyền vào khi kết thúc.
 
Upvote 0
Àh chắc mình lờ mờ đoán được bạn muốn làm gì rồi
 
Upvote 0
Bổ sung cho bài #9:

Thường thì gọi method của object, người ta có thể dùng CallByName.

Sub GoiSubCuaForm(TenForm, TenMethod)
CallByName UserForms(TenForm), TenMethod, vbMethod
End Sub

Như đã nói trước đây, tôi không có thử với UserForm nên không bảo đảm.
 
Upvote 0
Bổ sung cho bài #9:

Thường thì gọi method của object, người ta có thể dùng CallByName.

Sub GoiSubCuaForm(TenForm, TenMethod)
CallByName UserForms(TenForm), TenMethod, vbMethod
End Sub

Như đã nói trước đây, tôi không có thử với UserForm nên không bảo đảm.
Cả 2 đoạn code ở #9 và #13 đều báo cùng một lỗi.
Hinh.jpg
Code #9 báo tại dòng
Mã:
With UserForms(TenForm)
Code $13 tại dòng
Mã:
CallByName UserForms(TenForm), TenMethod, vbMethod
Trước khi hỏi thì em cũng dùng lệnh thế này.
Mã:
Dim Frm As MSForms.UserForm
    Set Frm = New UserForm 'Với UserForm là tên của Form đang mở
    Frm.TenSub
Nhưng nó lại báo lỗi, nhưng giờ em sửa chút và nó đã chạy ngon rồi.
Mã:
Dim Frm As Object
    Set Frm = New UserForm 'Với UserForm là tên của Form đang mở
    Frm.TenSub
Nhưng cách này vẫn chưa tùy biến được UserForm.
Àh chắc mình lờ mờ đoán được bạn muốn làm gì rồi
Ý đồ của mình ở file dưới đây người đẹp.
 

File đính kèm

  • TestCode.xlsm
    23 KB · Đọc: 23
Upvote 0
Cả 2 đoạn code ở #9 và #13 đều báo cùng một lỗi.
View attachment 235076
Code #9 báo tại dòng
Mã:
With UserForms(TenForm)
Code $13 tại dòng
Mã:
CallByName UserForms(TenForm), TenMethod, vbMethod
Trước khi hỏi thì em cũng dùng lệnh thế này.
Mã:
Dim Frm As MSForms.UserForm
    Set Frm = New UserForm 'Với UserForm là tên của Form đang mở
    Frm.TenSub
Nhưng nó lại báo lỗi, nhưng giờ em sửa chút và nó đã chạy ngon rồi.
Mã:
Dim Frm As Object
    Set Frm = New UserForm 'Với UserForm là tên của Form đang mở
    Frm.TenSub
Nhưng cách này vẫn chưa tùy biến được UserForm.

Ý đồ của mình ở file dưới đây người đẹp.
-------------------------


Lớp UserForms của VBA không sử dụng đối số là Chuỗi để lấy đối tượng nên dẫn đến lỗi.

------------------------
Mã:
Dim Frm As MSForms.UserForm
    Set Frm = New UserForm 'Với UserForm là tên của Form đang mở
    Frm.TenSub
Dòng code trên bị lỗi vì khao báo Lớp bị sai ở Dòng:
Mã:
Dim Frm As MSForms.UserForm
Form được tạo với tên gì thì khai báo tên đó, vì nó là lớp:
Mã:
Dim Frm As UserForm1
Hoặc Dim Frm As New UserForm1
 
Upvote 0
Toi khong chắc là khi bạn chạy lệnh:
Set frm = New UserFormX
thì nó nối frm vào cái UserFormX đã mở sẵn hay nó tạo thêm một cái Object nữa, theo mẫu UserFormX.
Theo lý thuyết OO thì là trường hợp 2. Nhưng mà ai biết nổi cái ông nội VBA và UserForm này.
 
Upvote 0
Cả 2 đoạn code ở #9 và #13 đều báo cùng một lỗi.
.
Tôi thử một chút, và "tạm" tin rằng do cái collection UserForms nó chỉ cho truy cập phần tử bằng chỉ số chứ không chịu dùng tên.
Vì vậy cách truy cập duy nhất là cách của bài #4. Tức là bạn phải duyệt cả collection và so sánh tên để lấy đúng form
TenForm = UCase(TenForm)
For Each Frm in UserForm
If UCase(Frm.Name) = TenForm Then Set miFrm = Frm
Next Frm
If Not miFrm Is Nothing Then _
CallByName miFrm, TenMethod, vbMethod

Hoặc bạn dùng một wrapper. Đặt một Public Global Collection (Dictionary cũng được) ở Moule chính,.
Mỗi lần mở một UserForm thì bạn add nó vào Collection, với Form và tên Form.
Cái Wrapper này sẽ giúp bạn có thể truy cập thẳng theo tên.
 
Upvote 0
Tôi thử một chút, và "tạm" tin rằng do cái collection UserForms nó chỉ cho truy cập phần tử bằng chỉ số chứ không chịu dùng tên.
Vì vậy cách truy cập duy nhất là cách của bài #4. Tức là bạn phải duyệt cả collection và so sánh tên để lấy đúng form
TenForm = UCase(TenForm)
For Each Frm in UserForm
If UCase(Frm.Name) = TenForm Then Set miFrm = Frm
Next Frm
If Not miFrm Is Nothing Then _
CallByName miFrm, TenMethod, vbMethod

Hoặc bạn dùng một wrapper. Đặt một Public Global Collection (Dictionary cũng được) ở Moule chính,.
Mỗi lần mở một UserForm thì bạn add nó vào Collection, với Form và tên Form.
Cái Wrapper này sẽ giúp bạn có thể truy cập thẳng theo tên.
Sư phụ Vẹt nhỏ có nghĩ tới chuyện cùng lúc mở hai file excel, và hai file excel ý có hai cái form nó cùng tên, form nào hay hay là chau hay copy vào nhiều file để dùng, bản tính lười nó khổ thế đó. Gặp phải vụ đó nó lại bắt nhầm "vợ" thì khổ nhà trai.
 
Upvote 0
Sư phụ Vẹt nhỏ có nghĩ tới chuyện cùng lúc mở hai file excel, và hai file excel ý có hai cái form nó cùng tên, form nào hay hay là chau hay copy vào nhiều file để dùng, bản tính lười nó khổ thế đó. Gặp phải vụ đó nó lại bắt nhầm "vợ" thì khổ nhà trai.
Kết hợp với cái này là giải quyết được ngay.
 
Upvote 0
Web KT
Back
Top Bottom