Thiết kế UserForm bài số 08: Tạo Menu Button trên UserForm

tuhocvba

Administrator
Thành viên BQT
Bài học thiết kế số 01
Bài học thiết kế số 02
Bài học thiết kế số 03 .
Bài học thiết kế số 04 .
Bài học thiết kế số 05 .
Bài học thiết kế số 06 .
Bài học thiết kế số 07 .
_______________
Khi phải sử dụng nhiều nút bấm trên UserForm, các bạn sẽ phải làm thế nào bây giờ? Nếu cho hết các nút bấm lên UserForm, thì sẽ làm tốn diện tích.
Vì vậy, ở bài viết này, chúng ta sẽ xây dựng Menu Button như dưới đây:
Bạn cần đăng nhập để thấy đính kèm


Chúng ta có một dãy 5 nút bấm. Mỗi lần ấn nút ▲ và ▼ thì dãy nút bấm này sẽ được thay đổi.
Chúng ta tạo:
Class có tên là : PagedButtons.
Class có tên là: SelectButton .
Code cho class SelectButton là:
Mã:
Option Explicit
Public WithEvents btn As MSForms.CommandButton
Public Parent As PagedButtons
Private Sub btn_Click()
    Parent.callBack btn.Caption
End Sub

Public Property Let Enabled(e As Boolean)
    btn.Enabled = e
End Property

Public Property Let Caption(x As String)
    btn.Caption = x
End Property

Public Property Get Self() As Object
    Set Self = Me
End Property

Public Sub ReleaseObject()
    Set btn = Nothing
    Set Parent = Nothing
End Sub
Code cho class PagedButtons là:
Mã:
Option Explicit
Private WithEvents previousButton As MSForms.CommandButton
Private WithEvents nextButton As MSForms.CommandButton
Private pageNumber As Long
Private selectButtons As Collection
Private menuItems As Collection
Public Event Selected(x As String)

Sub callBack(x As String)
    RaiseEvent Selected(x)
End Sub

Sub Init(previous_button As MSForms.CommandButton, _
    next_button As MSForms.CommandButton, _
    ParamArray select_buttons())
  
    Set previousButton = previous_button
    Set nextButton = next_button
  
    Set selectButtons = New Collection
    Dim b
    For Each b In select_buttons
        With New SelectButton
            Set .Parent = Me
            Set .btn = b
            selectButtons.Add .Self
        End With
    Next
  
    Set menuItems = New Collection
    pageNumber = 1
End Sub

Sub addMenuItem(menu_caption As String)
    menuItems.Add menu_caption
End Sub

Sub DrawCaptions()
    previousButton.Enabled = pageNumber <> 1
    nextButton.Enabled = pageNumber < maxPage

    Dim itemCursor: itemCursor = selectButtons.Count * pageNumber - selectButtons.Count
    Dim i As Long
    For i = 1 To selectButtons.Count
        If itemCursor + i <= menuItems.Count Then
            selectButtons(i).Enabled = True
            selectButtons(i).Caption = menuItems(itemCursor + i)
        Else
            selectButtons(i).Enabled = False
            selectButtons(i).Caption = "-"
        End If
    Next
End Sub

Private Property Get maxPage() As Long
    maxPage = roundUp(menuItems.Count / selectButtons.Count)
End Property

Private Function roundUp(x As Double) As Long
    roundUp = Int(x + 0.999)
End Function

Private Sub nextButton_Click()
    pageNumber = pageNumber + 1
    DrawCaptions
End Sub

Private Sub nextButton_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
    Call nextButton_Click
    If pageNumber >= maxPage - 1 Then
        Cancel = True
    End If
End Sub

Private Sub previousButton_Click()
    pageNumber = pageNumber - 1
    DrawCaptions
End Sub

Private Sub previousButton_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
    Call previousButton_Click
    If pageNumber <= 2 Then
        Cancel = True
    End If
End Sub

Public Sub ReleaseObject()
    Dim b As SelectButton
    For Each b In selectButtons
        b.ReleaseObject
    Next
    Set menuItems = Nothing
    Set selectButtons = Nothing
End Sub
 

giaiphapvba

Administrator
Thành viên BQT
Tạo UserForm:
Cấu tạo UserForm được mô tả theo như dưới đây:
Bạn cần đăng nhập để thấy đính kèm


Code cho UserForm là:
Mã:
Option Explicit

Private WithEvents menu As PagedButtons

Private Sub menu_Selected(x As String)
    Me.Label1.Caption = x & " da duoc chon."
End Sub

Private Sub UserForm_Initialize()
    Me.Label1 = vbNullString
    Set menu = New PagedButtons
    
    menu.Init Me.btnPrevious, Me.btnNext, _
        Me.btn1, Me.btn2, Me.btn3, Me.btn4, Me.btn5
    
    Dim i As Long
    For i = Asc("A") To Asc("Z")
        menu.addMenuItem "Button" & Chr(i)
    Next
    menu.DrawCaptions
End Sub

Private Sub UserForm_Terminate()
    menu.ReleaseObject
    Unload Me
End Sub
File demo:

Nguồn tham khảo:
 

tuhocvba

Administrator
Thành viên BQT
Điểm chính trong kỹ thuật này:
Khi nút ấn được ấn thì sẽ phát sinh sự kiện, thủ tục menu_Selected sẽ được gọi.
Mã:
Private Sub menu_Selected(x As String)
    Me.Label1.Caption = x & " da duoc chon."
End Sub
Chúng ta không xây dựng chức năng cho từng nút bấm riêng biệt, mà chúng ta điều khiển chúng thông qua PagedButtons.
Tóm lại, bảng điều khiển này được thiết kế như dưới đây.
Bạn cần đăng nhập để thấy đính kèm

Nếu như chúng ta muốn thêm một nút bấm thì việc này cũng rất đơn giản, ví dụ:
Bạn cần đăng nhập để thấy đính kèm

Trên UserForm, chúng ta thêm một nút bấm.
Việc thay đổi code cũng chỉ diễn ra ở một nơi, rất thuận tiện. Chúng ta tăng thêm biến số cho munu.Init của phương thức UserForm_Initialize của UserForm .
Bạn cần đăng nhập để thấy đính kèm

Nếu chúng ta muốn giảm số lượng nút bấm, chúng ta cũng làm tương tự, giảm số lượng biến số đầu vào cho menu.Init .
Ở đây tôi không giới thiệu, tuy nhiên thông qua API chúng ta cũng có thể làm thêm những việc như tùy biến thay đổi kích cỡ của UserForm.
 

giaiphapvba

Administrator
Thành viên BQT
Cơ cấu tổ chức của chương trình:
Bạn cần đăng nhập để thấy đính kèm

Trạng thái khởi tạo ban đầu của đối tượng PagedButtons như ở sơ đồ miêu tả bên trên.
Khi một nút bấm được truyền vào đối tượng PagedButtons , thì đối tượng SelectButton sẽ được tạo ra. Nút bấm tự bản thân nó sẽ được lưu trữ trong SelectButtonsCollection.
Tại thời điểm này, thì các đối tượng selectButton sẽ tham chiếu tới PagedButtons.

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

Quá trình này là tuần hoàn. Chúng ta tạo ra thủ tục ReleaseObject để ngắt việc tuần hoàn này.
Bạn cần đăng nhập để thấy đính kèm


Thú thực là mình vừa đọc vừa dịch nhưng thấy vẫn còn mơ hồ. Có lẽ cần phải chạy từng dòng code để lý giải tường tận hơn. Mọi người chờ nhé.
 

Euler

Administrator
Thành viên BQT
Giải thích thêm:
Cơ cấu tổ chức của chương trình:
Bạn cần đăng nhập để thấy đính kèm

PagedButtons chính là khay nút bấm trên UserForm. Các nút bấm này sẽ lưu trong menuItems Collections, trong này chứa bao nhiêu nút bấm thì PagedButtons sẽ thể hiện ra bấy nhiêu. Tuy nhiên, mỗi lần chúng ta chỉ thể hiện ra một số lượng nút bấm hữu hạn, chẳng hạn là 5 nút bấm. Vì vậy chúng ta phải chia trang.
Vì vậy chúng ta để ý là trong class PagedButtons thì có đoạn code này:
Mã:
maxPage = roundUp(menuItems.Count / selectButtons.Count)
Tuy nhiên, nút bấm để thực thi một mệnh lệnh nào đó nếu ta ấn vào thì phải có sự kiện cho nó. Vì vậy mới có Class selectButton. Cụ thể trong này các bạn sẽ tìm thấy thủ tục:
Mã:
Private Sub btn_Click()
    Parent.callBack btn.Caption
End Sub

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

Khi ẩn UserForm thì chúng ta cần giải phóng Collection lưu trữ các nút bấm, do đó có thủ tục: ReleaseObject.
Các bạn tìm thấy thủ tục này được định nghĩa trong Class SelectButton.
Mã:
Public Sub ReleaseObject()
    Set btn = Nothing
    Set Parent = Nothing
End Sub
Thủ tục này được gọi ở:
Mã:
Private Sub UserForm_Terminate()
    menu.ReleaseObject
    Unload Me
End Sub
Bạn cần đăng nhập để thấy đính kèm

Nếu như Initialize có nghĩa là quá trình khởi động UserForm bắt đầu, thì Terminate là sự kiện khi UserForm bắt đầu được đóng.
Nguồn dẫn:

Chương trình này sử dụng Class ở mức độ rất phức tạp, cần nghiên cứu sâu hơn. Nếu hiểu rõ thì sẽ rất hữu ích cho kỹ năng code của chúng ta sau này.
 
Top