Các cách "ẩn" UserForm

tuhocvba

Administrator
Thành viên BQT
Cách 1: Dùng Dialog sheet để thiết kế giao diện UserForm rồi ẩn sheet này đi
Bạn thiết kế UserForm bằng sheet DiaLog.
Bạn cần đăng nhập để thấy đính kèm


Trình tự như sau:
Trên giao diện Excel bạn click chuột phải vào sheet và chọn Insert.
Bạn cần đăng nhập để thấy đính kèm
Chọn loại Dialog như hình ở dưới và ấn OK.
Bạn cần đăng nhập để thấy đính kèm

Việc thiết kế UserForm kiểu này rất dễ dàng. Bạn có thể lấy các đối tượng ở:
Vào thẻ Developer => Insert. Tại đây bạn có thể lấy các đối tượng cơ bản như hình dưới:

Bạn cần đăng nhập để thấy đính kèm

Để hiển thị, trên Module1 bạn viết code như sau:
Mã:
Sub test()
   ThisWorkbook.DialogSheets("Dialog1").Show
End Sub
Như vậy bài toán làm thế nào để ẩn được UserForm bây giờ quay về bài toán ẩn sheet.
Code trên Thisworbook, ta cần can thiệp vào sự kiện Workbook Open:
Mã:
Private Sub Workbook_Open()
    ThisWorkbook.DialogSheets("Dialog1").Visible = 2 'very Hiden'
End Sub
Hãy xem giao diện dưới đây, chúng ta hoàn toàn không thấy sự có mặt của UserForm.
Bạn cần đăng nhập để thấy đính kèm

Tôi đã rất ngạc nhiên trong lần tham khảo code của người khác, không hiểu vấn đề, tại sao giao diện userform khi chạy chương trình thì có. Mà khi đi tìm thiết kế userform thì không tìm thấy ở đâu. Nhờ có cựu admin vothanhthu tìm hiểu mà vấn đề này đã được lý giải.
Các bạn có thể kết hợp với ẩn Module, mã hóa unviewable bằng của vothanhthu.
Như vậy, ngay cả khi giải mã unviewable, bước tiếp theo người ta ngạc nhiên sẽ là không nhìn thấy thiết kế UserForm của bạn ở đâu.

Sự việc sẽ còn thú vị hơn, nếu như bạn không tạo ra sheet Dialog thủ công như hướng dẫn ở trên. Mà thay vào đó bạn tạo sheet Dialog bằng code.
Ví dụ bạn copy code sau cho vào Module 2 và chạy thủ tục CustomButtonsDialog:
Mã:
Option Explicit
Public dlgPrint As DialogSheet
Public blnPrint As Boolean

Sub CustomButtonsDialog()

'Define variables.
blnPrint = True
Dim ButtonDialog As String
ButtonDialog = "CustomButtons"

'Turn off ScreenUpdating.
Application.ScreenUpdating = False

'Delete previous dialog sheet if by chance it exists.
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.DialogSheets(ButtonDialog).Delete
Err.Clear
Application.DisplayAlerts = True

'Add the custom dialog sheet.
Set dlgPrint = ActiveWorkbook.DialogSheets.Add
'Open a With structure for the dialog sheet.
With dlgPrint
'Name the dialog sheet and hide it.
.Name = ButtonDialog
.Visible = xlSheetHidden

'Size the dialog sheet's dialog frame.
With .DialogFrame
.Height = 130
.Width = 204
.Caption = "Orientation preferences."
End With

'Hide the two default buttons of OK and Cancel.
'Custom buttons will be created and used instead.
.Buttons("Button 2").Visible = False
.Buttons("Button 3").Visible = False

'Add a label at the top of the dialog frame.
.Labels.Add 80, 50, 180, 18
.Labels(1).Caption = "What's your printing preference?"

'Add 3 buttons, Distance from Left and Top; Width and Height.

'Custom Button #1 (index button #3)
.Buttons.Add 84, 84, 80, 18
With .Buttons(3)
.Caption = "Print Portrait"
.OnAction = "myCustomButton"
End With
'Custom Button #2 (index button #4)
.Buttons.Add 180, 84, 80, 18
With .Buttons(4)
.Caption = "Print Landscape"
.OnAction = "myCustomButton"
End With
'Custom Button #3 (index button #5)
.Buttons.Add 84, 116, 176, 18
With .Buttons(5)
.Caption = "Forget it -- I don't want to print anything."
.OnAction = "myCustomButton"
End With

'Turn ScreenUpdating on again.
Application.ScreenUpdating = True

'The X Cancel button was clicked on the title bar.
If .Show = False Then
MsgBox "You clicked the ''X'' close button.", 64, "Print cancelled."
blnPrint = False
End If

'Close the dialog sheet's With structure.
End With

'Delete the dialog frame.
Run "DeleteDialog"

End Sub


Private Sub myCustomButton()
'Hide the custom dialog sheet.
dlgPrint.Hide
'Use a Case structure to run the macro corresponding to the
'index of the custom button that was clicked.
Select Case dlgPrint.Buttons(Application.Caller).Index
'Portrait.
Case 3: ActiveSheet.PageSetup.Orientation = xlPortrait
'Landscape.
Case 4: ActiveSheet.PageSetup.Orientation = xlLandscape
'Cancel (the "Forget it"-captioned button).
Case 5: blnPrint = False
MsgBox "No problem -- nothing will print.", 64, "Print cancelled."
End Select
End Sub


Private Sub DeleteDialog()
'Delete a previous dialog sheet if by chance it exists.
With Application
.ScreenUpdating = False
.DisplayAlerts = False
On Error Resume Next
DialogSheets("CustomButtons").Delete
Err.Clear
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Kết quả là có một giao diện userform hiện ra:
Bạn cần đăng nhập để thấy đính kèm
Nguồn code trên:
Về trình tự từng bước để tạo ra Dialog sheet bằng code như trên, chúng ta sẽ cùng bàn luận thêm.

Cách 2: Viết UserForm bằng code.
Cũng tương tự như ví dụ ở trên, không phải là tạo Dialog sheet bằng code, chúng ta tạo trực tiếp UserForm bằng code.
Hãy dán code trên vào Module 3.
Mã:
Sub CreateUserForm()
Dim myForm As Object
Dim NewFrame As MSForms.Frame
Dim NewButton As MSForms.CommandButton
'Dim NewComboBox As MSForms.ComboBox
Dim NewListBox As MSForms.ListBox
'Dim NewTextBox As MSForms.TextBox
'Dim NewLabel As MSForms.Label
'Dim NewOptionButton As MSForms.OptionButton
'Dim NewCheckBox As MSForms.CheckBox
Dim X As Integer
Dim Line As Integer

'This is to stop screen flashing while creating form
Application.VBE.MainWindow.Visible = False

Set myForm = ThisWorkbook.VBProject.VBComponents.Add(3)

'Create the User Form
With myForm
    .Properties("Caption") = "New Form"
    .Properties("Width") = 300
    .Properties("Height") = 270
End With

'Create ListBox
Set NewListBox = myForm.designer.Controls.Add("Forms.listbox.1")
With NewListBox
    .Name = "lst_1"
    .Top = 10
    .Left = 10
    .Width = 150
    .Height = 230
    .Font.Size = 8
    .Font.Name = "Tahoma"
    .BorderStyle = fmBorderStyleOpaque
    .SpecialEffect = fmSpecialEffectSunken
End With

'Create CommandButton Create
Set NewButton = myForm.designer.Controls.Add("Forms.commandbutton.1")
With NewButton
    .Name = "cmd_1"
    .Caption = "clickMe"
    .Accelerator = "M"
    .Top = 10
    .Left = 200
    .Width = 66
    .Height = 20
    .Font.Size = 8
    .Font.Name = "Tahoma"
    .BackStyle = fmBackStyleOpaque
End With

'add code for listBox
lstBoxData = "Data 1,Data 2,Data 3,Data 4"
myForm.codemodule.insertlines 1, "Private Sub UserForm_Initialize()"
myForm.codemodule.insertlines 2, "   me.lst_1.addItem ""Data 1"" "
myForm.codemodule.insertlines 3, "   me.lst_1.addItem ""Data 2"" "
myForm.codemodule.insertlines 4, "   me.lst_1.addItem ""Data 3"" "
myForm.codemodule.insertlines 5, "End Sub"

'add code for Comand Button
myForm.codemodule.insertlines 6, "Private Sub cmd_1_Click()"
myForm.codemodule.insertlines 7, "   If me.lst_1.text <>"""" Then"
myForm.codemodule.insertlines 8, "      msgbox (""You selected item: "" & me.lst_1.text )"
myForm.codemodule.insertlines 9, "   End If"
myForm.codemodule.insertlines 10, "End Sub"
'Show the form
VBA.UserForms.Add(myForm.Name).Show

'Delete the form (Optional)
'ThisWorkbook.VBProject.VBComponents.Remove myForm
End Sub
Hãy chạy thủ tục CreateUserForm ở code trên.
Trước khi chạy code:
Bạn cần đăng nhập để thấy đính kèm
Không có UserForm nào đúng không?
Kết quả hiển thị ra như sau:
Bạn cần đăng nhập để thấy đính kèm
Bạn cần đăng nhập để thấy đính kèm
Tôi đóng UserForm trên. Kiểm tra trong VBE:
Bạn cần đăng nhập để thấy đính kèm
UserForm1 đã được tạo ra, thật đáng kinh ngạc. Chúng ta có thể cải tiến một chút trong code trên. Đó là kiểm tra xem file đã có UserForm nào tên là UserForm1 hay chưa, nếu có thì xóa đi, nếu chưa có thì mới thêm vào.
Và khi UserForm1 được Unload (đóng) thì nên chạy thủ tục xóa UserForm1. Như vậy thiết kế UserForm của bạn sẽ thể hiện hoàn toàn trên code, điều này sẽ gây khó khăn rất lớn cho người khác muốn học hỏi thiết kế UserForm của bạn.
Nguồn code trên tôi tham khảo .

Lời kết:
Quan điểm của diễn đàn là chia sẻ kiến thức trên tinh thần học thuật. Topic này không cổ vũ các bạn giấu code.
Trên tinh thần học thuật, tôi muốn chúng ta đi theo hai hướng:

  1. Tổng kết thành kiến thức, tạo Dialog sheet bằng code và các ví dụ.
  2. Tổng kết thành kiến thức, tạo UserForm bằng code và các ví dụ.
Vì vậy, trong thời gian tới, tôi hi vọng mỗi người có sự tìm hiểu riêng, rồi chia sẻ kiến thức các bạn tìm hiểu được dưới dạng chuyên đề theo hai hướng đã nêu ở trên. Đó cũng là điều tôi kỳ vọng ở các bạn, thay vì chỉ lên diễn đàn tìm hiểu có cái gì để đọc không, tôi đã gợi mở vấn đề, các bạn cùng tìm hiểu, sau đó chúng ta chia sẻ cho nhau kiến thức dưới dạng chuyên đề mà mọi người tìm hiểu được.
 

bvtvba

Thành viên tích cực
Em xin tiếp nối, về Dialog Sheet.
Về code nêu ví dụ ở #1, em xin rút gọn lại.
Mã:
Public dlgPrint As DialogSheet


Sub CustomButtonsDialog()

'Define variables.
Dim ButtonDialog As String  'ten sheet ma ta se tao ra
ButtonDialog = "CustomButtons"

'=========================XOA SHEET=================================
'Khong cap nhat man hinh
Application.ScreenUpdating = False

'Delete previous dialog sheet if by chance it exists.
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.DialogSheets(ButtonDialog).Delete
Err.Clear
Application.DisplayAlerts = True
'=========================HOAN THANH QUA TRINH XOA SHEET============


'=========================TAO SHEET=================================
Set dlgPrint = ActiveWorkbook.DialogSheets.Add
'Open a With structure for the dialog sheet.
With dlgPrint
'Name the dialog sheet and hide it.
    .Name = ButtonDialog
    .Visible = xlSheetHidden

    'Size the dialog sheet's dialog frame.
    With .DialogFrame
        .Height = 150
        .Width = 204
    .Caption = "tuhocvba.net"
    End With

    'Hide the two default buttons of OK and Cancel.
    'Custom buttons will be created and used instead.
    .Buttons("Button 2").Visible = False
    .Buttons("Button 3").Visible = True

    'Add a label at the top of the dialog frame.
    .Labels.Add 80, 50, 180, 18 'Left - Top - Width - Height
    
    .Labels(1).Caption = "What's your printing preference?"

    'Add 3 buttons, Distance from Left and Top; Width and Height.
    
    'Custom Button #1 (index button #3)
    .Buttons.Add 84, 84, 80, 18 'Left - Top - Width - Height
    With .Buttons(3)
    .Caption = "Print Portrait"
    .OnAction = "myCustomButton"
    End With
    'Custom Button #2 (index button #4)
    .Buttons.Add 180, 84, 80, 18
    With .Buttons(4)
    .Caption = "Print Landscape"
    .OnAction = "myCustomButton"
    End With
    'Custom Button #3 (index button #5)
    .Buttons.Add 84, 116, 194, 18
    With .Buttons(5)
    .Caption = "Forget it -- I don't want to print anything."
    .OnAction = "myCustomButton"
    End With
    
    With .Buttons(2)
        .Left = 84
        .Top = 140
        
    End With

'Turn ScreenUpdating on again.
Application.ScreenUpdating = True
'Hien thi UserForm
    .Show


'Close the dialog sheet's With structure.
End With

'=========================XOA SHEET=================================
Run "DeleteDialog"

End Sub


Private Sub myCustomButton()
    MsgBox "tuhocvba.net"
End Sub


Private Sub DeleteDialog()
'Delete a previous dialog sheet if by chance it exists.
With Application
.ScreenUpdating = False
.DisplayAlerts = False
On Error Resume Next
DialogSheets("CustomButtons").Delete
Err.Clear
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Dòng code số 1: dlgPrint chính là Dialog sheet "CustomButtons".
Chương trình sẽ tạo ra Dialog sheet có tên là CustomButtons.

Về bố cụ code:
Thủ tục CustomButtonsDialog đóng vai trò chính.

-Kiểm tra xem có Dialog sheet nào có tên là CustomButtons hay chưa? Nếu có rồi thì xóa. Điều này được thể hiện ở dòng code số 10 tới dòng code số 19. Thật ra nó cũng chẳng quan tâm là tồn tại hay chưa, mà cứ thế xóa. Do đó có khả năng xảy ra lỗi nếu sheet này chưa tồn tại mà cứ cố xóa một thứ không tồn tại. Vì vậy dòng code số 16 có mục đích phớt lờ lỗi, dòng code số 17 tiến hành xóa. Vì có khả năng phát sinh lỗi như đã nói ở trên, cho nên dòng code số 18 thực hiện xóa bỏ lỗi nếu có.

-Sau đó nó sẽ tạo ra Dialog sheet có tên là CustomButtons. Điều này được thể hiện ở dòng code số 24.

-Từ dòng code số 26 tới dòng code 82: Đây là phần thiết kế giao diện UserForm. Đây chính là phần cần thảo luận sâu hơn.
Cụ thể lần này, code tạo ra một Label và 3 nút bấm .
Ngoài ra, mọi người chú ý, khi tạo Dialogsheet thì mặc định sẽ có hai nút OK và Cancel. Nút OK có lẽ không cần dùng, mình không hiểu mục đích, cho nên theo như code ban đầu, để ẩn, được thể hiện ở dòng code 40.
Nút Cancel có tác dụng như Unload Me trong code UserForm đấy ạ. Vì lý do đó, mình để nút bấm này hiển thị, được thể hiện ở dòng code 41. Thiết định vị trí cho nó trên giao diện hiển thị được thể hiện ở dòng code 69~73.

Dòng code 54 ( 60, 66 ) là để gán macro cho các nút bấm.

-Giống như UserForm, chúng ta cần Show, vì vậy có dòng code 78, mục đích để giao diện hiển thị ra giao tiếp với người dùng.
-Dòng code 85 thực hiện xóa Dialog Sheet đi. Nó giống như Call macro1 nhưng ở đây cú pháp là Run "macro1" .
Ta có thủ tục DeleteDialog là để xóa Dialog sheet.
 

thanhphong

Thành viên
Dialog Sheet tiếp nối :
-Tạo Checkbox:
:
Dòng code 63~67 :
Mã:
.Buttons.Add 84, 116, 194, 18
    With .Buttons(5)
    .Caption = "Forget it -- I don't want to print anything."
    .OnAction = "myCustomButton"
    End With
Sửa thành:
Mã:
    .CheckBoxes.Add 84, 116, 194, 18
    With .CheckBoxes(1)
        .Caption = "SelectAll"
    End With
Đến đây mục đích tạo CheckBox đã đạt được. Các bạn thử nhé.

-Lấy giá trị của CheckBox:
Chỗ này đòi hỏi công phu một chút.
Quay lại code của .
Tôi khai báo thêm biến public ckb để ghi trạng thái ô CheckBox.
Mã:
Public ckb      As Byte
Code tạo CheckBox , vị trí sửa giống như phần trên, nhưng sửa thành:
Mã:
    .CheckBoxes.Add 84, 116, 194, 18
    With .CheckBoxes(1)
        .Caption = "SelectAll"
        .OnAction = "selectCB"
    End With
Thêm thủ tục mới selectCB để chuyển tên ô CheckBox khi nó được Click, thủ tục này có tác dụng như sự kiện Click mà chúng ta vẫ quen code trên UserForm:
Mã:
Sub selectCB()
    If dlgPrint.CheckBoxes(1).Value = xlOn Then
        dlgPrint.CheckBoxes(1).Caption = "UnSelect"
        ckb = 1
    Else
        dlgPrint.CheckBoxes(1).Caption = "SelectAll"
        ckb = 0
    End If

End Sub
Một điều đặc biệt ở đây trị số dùng là xlOn mà không phải là True như ta quen dùng. Hãy lưu ý điều này.

Để kiểm tra nội dung mình code đúng hay chưa, tôi sửa lại thủ tục sau myCustomButton:
Mã:
Private Sub myCustomButton()
    MsgBox ckb
End Sub
 

PTHhn

Thành viên
DialogSheet này hay nhỉ, có điều không chọn chuột phải để xem Property của các đối tượng nhỉ.
Bạn cần đăng nhập để thấy hình ảnh


Nhưng record macro rồi sau đó dùng tay tạo giao diện trên DialogSheet thì cũng thu được code nên không khó khăn lắm.

-Tạo TextBox (Tên chính xác là EditBox) và lấy ký tự được nhập trong đó:
Mã:
Public dlgPrint As DialogSheet
Public lk       As String

Sub CustomButtonsDialog()

'Define variables.
Dim ButtonDialog As String  'ten sheet ma ta se tao ra
ButtonDialog = "CustomButtons"

'=========================XOA SHEET=================================
'Khong cap nhat man hinh
Application.ScreenUpdating = False

'Delete previous dialog sheet if by chance it exists.
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.DialogSheets(ButtonDialog).Delete
Err.Clear
Application.DisplayAlerts = True
'=========================HOAN THANH QUA TRINH XOA SHEET============


'=========================TAO SHEET=================================
Set dlgPrint = ActiveWorkbook.DialogSheets.Add
'Open a With structure for the dialog sheet.
With dlgPrint
'Name the dialog sheet and hide it.
    .Name = ButtonDialog
    .Visible = xlSheetHidden

    'Size the dialog sheet's dialog frame.
    With .DialogFrame
        .Height = 150
        .Width = 204
    .Caption = "tuhocvba.net"
    End With

    'Hide the two default buttons of OK and Cancel.
    'Custom buttons will be created and used instead.
    .Buttons("Button 2").Visible = False
    .Buttons("Button 3").Visible = True

    'Add a label at the top of the dialog frame.
    .Labels.Add 80, 50, 180, 18 'Left - Top - Width - Height
    
    .Labels(1).Caption = "What's your printing preference?"

    'Add 3 buttons, Distance from Left and Top; Width and Height.
    
    'Custom Button #1 (index button #3)
    .Buttons.Add 84, 84, 80, 18 'Left - Top - Width - Height
    With .Buttons(3)
    .Caption = "Print Portrait"
    .OnAction = "myCustomButton"
    End With

 
    .EditBoxes.Add 84, 116, 194, 18
    
    
    With .Buttons(2)
        .Left = 84
        .Top = 140
        .Caption = "Close"
    End With

'Turn ScreenUpdating on again.
Application.ScreenUpdating = True
'Hien thi UserForm
    .Show


'Close the dialog sheet's With structure.
End With

'=========================XOA SHEET=================================
Run "DeleteDialog2"

End Sub


Private Sub myCustomButton()
    lk = dlgPrint.EditBoxes(1).Text
    MsgBox lk
End Sub


Private Sub DeleteDialog()
'Delete a previous dialog sheet if by chance it exists.
With Application
.ScreenUpdating = False
.DisplayAlerts = False
On Error Resume Next
DialogSheets("CustomButtons").Delete
Err.Clear
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Bạn cần đăng nhập để thấy hình ảnh
 

BKKBG

Thành viên
Đúng như đã phát hiện. Với DialogSheet thì giá trị logic lại dùng xlOn mà không dùng True.
-Tạo Frame và Option, đọc giá trị Option:
Mã:
Public dlgPrint As DialogSheet
Public lc      As String

Sub CustomButtonsDialog()

'Define variables.
Dim ButtonDialog As String  'ten sheet ma ta se tao ra
ButtonDialog = "CustomButtons"

'=========================XOA SHEET=================================
'Khong cap nhat man hinh
Application.ScreenUpdating = False

'Delete previous dialog sheet if by chance it exists.
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.DialogSheets(ButtonDialog).Delete
Err.Clear
Application.DisplayAlerts = True
'=========================HOAN THANH QUA TRINH XOA SHEET============


'=========================TAO SHEET=================================
Set dlgPrint = ActiveWorkbook.DialogSheets.Add
'Open a With structure for the dialog sheet.
With dlgPrint
'Name the dialog sheet and hide it.
    .Name = ButtonDialog
    .Visible = xlSheetHidden

    'Size the dialog sheet's dialog frame.
    With .DialogFrame
        .Height = 170
        .Width = 250
    .Caption = "tuhocvba.net"
    End With

    'Hide the two default buttons of OK and Cancel.
    'Custom buttons will be created and used instead.
    .Buttons("Button 2").Visible = False
    .Buttons("Button 3").Visible = True

    'Add a label at the top of the dialog frame.
    .Labels.Add 80, 50, 180, 18 'Left - Top - Width - Height
    
    .Labels(1).Caption = "What's your printing preference?"

    'Add 3 buttons, Distance from Left and Top; Width and Height.
    
    'Custom Button #1 (index button #3)
    .Buttons.Add 84, 84, 60, 18 'Left - Top - Width - Height
    With .Buttons(3)
    .Caption = "Check"
    .OnAction = "myCustomButton"
    End With

 
    .GroupBoxes.Add(114, 114, 150, 40).Select
    With .GroupBoxes(1)
        .Caption = "Lua chon"
    End With
    .OptionButtons.Add(126, 116, 42, 19.2).Select
    With .OptionButtons(1)
        .Caption = "LC1"
    End With
    .OptionButtons.Add(126, 130, 42, 19.2).Select
    With .OptionButtons(2)
        .Caption = "LC2"
    End With
    With .Buttons(2)
        .Left = 84
        .Top = 165
        .Caption = "Close"
    End With

'Turn ScreenUpdating on again.
Application.ScreenUpdating = True
'Hien thi UserForm
    .Show


'Close the dialog sheet's With structure.
End With

'=========================XOA SHEET=================================
Run "DeleteDialog"

End Sub


Private Sub myCustomButton()
    If dlgPrint.OptionButtons(1).Value = xlOn Then
        lc = 1
    ElseIf dlgPrint.OptionButtons(2).Value = xlOn Then
        lc = 2
    Else
        lc = 0
    End If
    MsgBox lc
End Sub


Private Sub DeleteDialog()
'Delete a previous dialog sheet if by chance it exists.
With Application
.ScreenUpdating = False
.DisplayAlerts = False
On Error Resume Next
DialogSheets("CustomButtons").Delete
Err.Clear
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Bạn cần đăng nhập để thấy hình ảnh
 
Top