Class module - Kỹ thuật Tạo và Wrap đối lượng

Liên hệ QC

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,649
Được thích
10,138
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Class module là một kỹ thuật nâng cao để người phát triển tạo thêm cho mình những class/đối tượng hay "bao" một đối tượng.
Có 2 lý do sau buộc bạn phải viết class:
+ Tạo đối tượng giống nhau.
+ Gán thêm thuộc tính cho một control/đối tượng đã có (như là: Property, Method, Even).

Xem file đính kèm dưới đây các bạn sẽ hiểu thêm ý nghĩa của nó.

(Để học cách viết code trong Class module, các bạn hãy tìm các bài viết đã có trên diễn đàn này hoặc tìm đọc tài liệu lập trình Class trong VB/VBA trên mạng)
 

File đính kèm

  • clsButton.zip
    19.6 KB · Đọc: 2,955
Kiểm soát nhập liệu trong Excel

Gửi các bạn một ví dụ về cách quản lý tất cả quá trình hoạt động của Excel.
 

File đính kèm

  • clsExcelApp.zip
    16.1 KB · Đọc: 2,504
Lần chỉnh sửa cuối:
Upvote 0
clsCommandButton

Nhân có ngươì bạn nhờ giúp về kỹ thuật CommandButton, tôi làm một ví dụ về kỹ thuật tạo và bao/Wrapping đối tượng CommandButton. Với file ví dụ "clsCommandButton.xls" chắc các bạn sẽ rất thú vị! Mong rằng sau này chúng ta sẽ có nhiều người coding nhiều với "Class module".

B1) Tạo một module (Trong VBE, vào menu Insert->Module) để test
Mã:
Option Explicit
'-------------------------------------------------
Const strClassObj As String = "Forms.CommandButton.1"
Public ctrlShNum() As clsCommandButton
Private CtrlCount As Integer
Public oldBtn As String
'-------------------------------------------------
Sub CreateCommandButtonOnSheet()
On Error GoTo Done

Dim cell As Range, objRanges As Range
Dim sh As Worksheet
Dim obj As OLEObject
Dim oldEvent As Boolean

oldEvent = Application.EnableEvents
Application.EnableEvents = False

    Set sh = ActiveSheet
    Set objRanges = Range("B3:B14")
    objRanges.RowHeight = 24
    
    CtrlCount = -1
    For Each cell In objRanges
        Set obj = sh.OLEObjects.Add(ClassType:=strClassObj, _
        Link:=False, DisplayAsIcon:=False, Left:=cell.Left, Top:=cell.Top, _
        Width:=cell.Width, Height:=cell.Height)
     
        CtrlCount = CtrlCount + 1
        ReDim Preserve ctrlShNum(CtrlCount) As clsCommandButton
        Set ctrlShNum(CtrlCount) = New clsCommandButton
        
        With ctrlShNum(CtrlCount)
            .Wrap obj.Object  
            .Caption = "CommandButton " & CtrlCount
        End With
        Set obj = Nothing
        Set cell = Nothing
    Next
Done:

Application.EnableEvents = oldEvent
    
    Set objRanges = Nothing
    Set sh = Nothing
If Err.Number <> 0 Then MsgBox Err.Description
End Sub
'-------------------------------------------------
Sub DeleteAllCommandButtons()
On Error Resume Next

Dim sh As Worksheet
Dim obj As OLEObject
Dim i As Integer
    Set sh = ActiveSheet
    For Each obj In sh.OLEObjects
        If obj.progID = strClassObj Then
            obj.Delete
        End If
    Next
    
    For i = LBound(ctrlShNum, 1) To UBound(ctrlShNum, 1)
        If Not ctrlShNum(i) Is Nothing Then
            Set ctrlShNum(i) = Nothing
        End If
    Next
    ReDim ctrlShNum(0) As clsCommandButton
    Set sh = Nothing

End Sub

B2) Tạo một Class/đối tượng (Trong VBE, vào menu Insert->Class module) , đặt tên là "clsCommandButton"

Mã:
'**************************************************************
Option Explicit
Private WithEvents ctrl As MSForms.CommandButton

'Constructor
Private Sub Class_Initialize()
    
End Sub

'FreeMem/Dispose
Private Sub Class_Terminate()
    If Not ctrl Is Nothing Then
        'ctrl.Delete
        Set ctrl = Nothing
    End If
End Sub
'-------------------------------------------------
'Methods
Public Sub Add(Form As MSForms.UserForm, Optional ByVal strCaption As String = "")
    Set ctrl = Form.Controls.Add("Forms.CommandButton.1")
    If strCaption <> "" Then
        ctrl.Caption = strCaption
    End If
End Sub
'-------------------------------------------------
Public Sub Wrap(ByVal ctrlCoomandButton As Object, Optional ByVal strCaption As String = "")
    Set ctrl = ctrlCoomandButton
    If strCaption <> "" Then
        ctrl.Caption = strCaption
    End If
End Sub
'-------------------------------------------------
'Properties

'Property Value
'-------------------------------------------------
Public Property Let Caption(ByVal vData As String)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.Caption = vData
End Property
Public Property Get Caption() As String
'used when retrieving value of a property, on the right side of an assignment.
    Caption = ctrl.Caption
End Property
'-------------------------------------------------
Public Property Let ForeColor(ByVal vData As Long)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.ForeColor = vData
End Property
Public Property Get ForeColor() As Long
'used when retrieving value of a property, on the right side of an assignment.
    ForeColor = ctrl.ForeColor
End Property
'-------------------------------------------------
Public Property Let BackColor(ByVal vData As Long)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.BackColor = vData
End Property
Public Property Get BackColor() As Long
'used when retrieving value of a property, on the right side of an assignment.
    BackColor = ctrl.BackColor
End Property
'-------------------------------------------------
'Property Top
Public Property Let Top(ByVal vData As Integer)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.Top = vData
End Property
Public Property Get Top() As Integer
'used when retrieving value of a property, on the right side of an assignment.
    Top = ctrl.Top
End Property
'-------------------------------------------------
'Property Left
Public Property Let Left(ByVal vData As Integer)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.Left = vData
End Property
Public Property Get Left() As Integer
'used when retrieving value of a property, on the right side of an assignment.
    Left = ctrl.Left
End Property
'-------------------------------------------------
'Property Width
Public Property Let Width(ByVal vData As Integer)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.Width = vData
End Property
Public Property Get Width() As Integer
'used when retrieving value of a property, on the right side of an assignment.
    Width = ctrl.Width
End Property
'-------------------------------------------------
'Property Height
Public Property Let Height(ByVal vData As Integer)
'used when assigning a value to the property, on the left side of an assignment.
    ctrl.Height = vData
End Property
Public Property Get Height() As Integer
'used when retrieving value of a property, on the right side of an assignment.
    Height = ctrl.Height
End Property
'-------------------------------------------------

[COLOR="Green"]'Events[/COLOR]
Private Sub ctrl_Click()
    ctrl.ForeColor = vbBlue
    MsgBox ctrl.Caption, , oldBtn
End Sub
'-------------------------------------------------
Private Sub ctrl_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Dim objCtrl As MSForms.CommandButton
    If oldBtn <> ctrl.Name Then
    '--Reset
        If oldBtn <> "" Then
            If TypeName(ctrl.Parent) = "Worksheet" Then '// Parent is Worksheet
                Set objCtrl = ctrl.Parent.OLEObjects(oldBtn).Object
            Else '// Parent is Userform
                Set objCtrl = ctrl.Parent.Controls(oldBtn)
            End If
            
            With objCtrl
                .ForeColor = vbBlack
                .BackColor = vb3DFace
                .Font.Bold = False
            End With
        End If
    '--Set New
        oldBtn = ctrl.Name
        ctrl.ForeColor = vbRed
        ctrl.Font.Bold = True
        ctrl.BackColor = vbYellow
    End If
End Sub

Các bạn tải file đính kèm tham khảo.
 

File đính kèm

  • clsCommandButton.zip
    26.6 KB · Đọc: 1,714
Lần chỉnh sửa cuối:
Upvote 0
Không cho đóng và lưu các workbook đang mở!

Xin gửi thêm các bạn ví dụ về khóa tất cả các workbook đang mở. Chương trình rất dễ viết nhưng phải dùng Class Module.

Bước 1: Tạo Class Module, đặt tên là clsExcelApp.
Trong môi trường VBA/VBE, Vào menu Insert\Class Module. Đặt tên class là clsExcelApp.

Soạn các lệnh sau vào trong class clsExcelApp.
Mã:
Option Explicit

Private WithEvents MyExcelApp As Excel.Application
Private mCancelClose As Boolean
Private mCancelSave As Boolean

Private Sub Class_Initialize()
    
End Sub

Private Sub Class_Terminate()
    Destroy
End Sub

Public Sub Create(ByVal ExcelApplication As Excel.Application)
    If Not MyExcelApp Is Nothing Then Exit Sub
    Set MyExcelApp = ExcelApplication
End Sub

Public Sub Destroy()
    Set MyExcelApp = Nothing
End Sub

Private Sub MyExcelApp_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
    Cancel = mCancelClose
    If Cancel Then
        MsgBox "All workbook are locked!. To unlock them, you send bears to me now!", vbCritical, "Author: Nguyen Duy Tuan"
    End If
End Sub

Public Property Get CancelClose() As Boolean
    CancelClose = mCancelClose
End Property

Public Property Let CancelClose(ByVal bNewValue As Boolean)
    mCancelClose = bNewValue
End Property

Public Property Get CancelSave() As Boolean
    CancelSave = mCancelSave
End Property

Public Property Let CancelSave(ByVal bNewValue As Boolean)
    mCancelSave = bNewValue
End Property

Private Sub MyExcelApp_WorkbookBeforeSave(ByVal Wb As Workbook, ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Cancel = mCancelSave
    SaveAsUI = Not Cancel
    If Cancel Then
        MsgBox """" & Wb.Name & """ can not save. You send bears to me for saving!", vbCritical, "Author: Nguyen Duy Tuan"
    End If
End Sub

Bước 2: Tạo Module để tạo các thủ tục chạy
Trong môi trường VBA/VBE, Vào menu Insert\Module.

Soạn các lệnh sau vào trong module
Mã:
Option Explicit
Dim MyExcelApp As clsExcelApp
Sub LockAllWorkbook()
    
    If Not MyExcelApp Is Nothing Then Exit Sub
    
    Set MyExcelApp = New clsExcelApp
    With MyExcelApp
        .Create Application
        .CancelClose = True
        .CancelSave = True
    End With
    
    MsgBox "All workbook are locked from this time!" & Chr(13) & _
            "To unlock, click ""StopLock"" button.", vbExclamation
    
End Sub

Sub CancelLoking()
    If MyExcelApp Is Nothing Then Exit Sub
    MyExcelApp.CancelClose = False
    MyExcelApp.CancelSave = False
End Sub

Sub StopLock()
    Set MyExcelApp = Nothing
    MsgBox "All workbook are free (unlock)!", vbInformation
End Sub

Toàn bộ mã nguồn có trong file đính kèm.
 

File đính kèm

  • LockAllWorkbook.zip
    13.4 KB · Đọc: 1,142
Lần chỉnh sửa cuối:
Upvote 0
Forms And NumPad

Các bạn tham khảo ví dụ tôi viết trong bài này "Forms And NumPad". Xem bài số #8
 
Upvote 0
Mình tải file của bạn về mở chỉ xuất hiện một hộp thoại à
 
Upvote 0
Mình đang tìm tòi học hỏi về VBA này, mình thấy khi tạo CommandButtons hơn 15 cái thì UserForm không độ dài.Minh có cách làm làm 1 cái Scroll để thấy được những
CommandButtons bị quá size UserForm không ạ? Xin Anh/Chị/Em trong nhóm hỗ trợ giúp ! Cám ơn nhiều ạ
 

File đính kèm

  • Untitled.jpg
    Untitled.jpg
    39.5 KB · Đọc: 62
Upvote 0
Các anh chị giúp em sửa code để code chỉ chạy trên file "cExcelApp" được không ạ?. Tức là khi em đang sử dụng file này thì nó chạy, khi mở file khác lên thì nó dừng lại ạ
 

File đính kèm

  • cExcelApp.xlsm
    16.7 KB · Đọc: 21
Upvote 0
Các anh chị giúp em sửa code để code chỉ chạy trên file "cExcelApp" được không ạ?. Tức là khi em đang sử dụng file này thì nó chạy, khi mở file khác lên thì nó dừng lại ạ

Nếu bạn muốn chỉ chạy trong file thì lập trình thẳng vào các sự kiện trong ThisWorkbook nhé. Không cần khởi tạo clsExcelApp.
 
Upvote 0
Thầy ơi, giả sử em vẫn muốn khởi tạo clsExcelApp thì có được không ạ?

Được. Em thay ExcelApp As Application thành MyWB As Workbook
Khi trỏ đổi đượng Set ExcelApp = Application thì đổi thành
Set MyWB = ThisWorkbook

Cơ bản là vậy.
 
Upvote 0
em chào thầy Nguyễn Duy Tuân, thầy có thể giúp em chỗ này được không ạ, em có tham khảo tại bài #3
và sửa lại theo cách em hiểu được để làm một listbox thay thế listbox truyền thống
trong lúc vận hành thì có truyền vào mảng Arr của listbox như trong hình
1640568693213.png
nếu vậy thì nó thêm mảng theo từng ctrl, vào lúc thêm như vậy thì ở ctrl khác cũng phải thêm, như vậy là mỗi ctrl đều có mảng riêng , vậy làm sao để chỉ thêm mảng 1 lần để dùng chung cho class đó được
em có dùng cách này để lưu mảng vào dùng chung
1640568878920.png
đó là ở vấn đề mảng cố định, còn chưa tính đến lọc cái mảng đó thì làm sao để thay đổi được ạ
tóm lại là thầy có thể gợi ý hoặc giúp em làm sao để việc truyền mảng vào linh hoạt được không ạ, với việc tô màu các label đó nhìn sao sao ấy ạ
Liên kết: https://youtu.be/J8lPM1fhmoo
 

File đính kèm

  • 1640569054318.png
    1640569054318.png
    31.6 KB · Đọc: 16
  • 1640569027432.png
    1640569027432.png
    14.1 KB · Đọc: 16
  • 1640568967846.png
    1640568967846.png
    58.5 KB · Đọc: 15
  • cListBox_Tan.xls
    164.5 KB · Đọc: 23
Lần chỉnh sửa cuối:
Upvote 0
em chào thầy Nguyễn Duy Tuân, thầy có thể giúp em chỗ này được không ạ, em có tham khảo tại bài #3
và sửa lại theo cách em hiểu được để làm một listbox thay thế listbox truyền thống
trong lúc vận hành thì có truyền vào mảng Arr của listbox như trong hình
View attachment 270802
nếu vậy thì nó thêm mảng theo từng ctrl, vào lúc thêm như vậy thì ở ctrl khác cũng phải thêm, như vậy là mỗi ctrl đều có mảng riêng , vậy làm sao để chỉ thêm mảng 1 lần để dùng chung cho class đó được
em có dùng cách này để lưu mảng vào dùng chung
View attachment 270803
đó là ở vấn đề mảng cố định, còn chưa tính đến lọc cái mảng đó thì làm sao để thay đổi được ạ
tóm lại là thầy có thể gợi ý hoặc giúp em làm sao để việc truyền mảng vào linh hoạt được không ạ, với việc tô màu các label đó nhìn sao sao ấy ạ
Liên kết: https://youtu.be/J8lPM1fhmoo

Nếu mảng bạn muốn dùng chung co các class thì khai báo Public ở một Module nhé. Còn vấn đề tô mầu, bản thân ListBox của VBA không tô mầu tuỳ ý vài item được, ai đó làm thì có thể nhái theo kiêir ListBox.
 
Upvote 0
Nếu mảng bạn muốn dùng chung co các class thì khai báo Public ở một Module nhé. Còn vấn đề tô mầu, bản thân ListBox của VBA không tô mầu tuỳ ý vài item được, ai đó làm thì có thể nhái theo kiêir ListBox.
vậy mảng đó không truyền vào khi chạy form được sao thầy. (hiện mảng e có khai báo ở trên form từ đó truyền vào class để chạy), nếu khai báo ở module biến mảng đó thì phải khai báo nhiều biến.
trong code của thầy có mục Wrap. vậy mục đó để làm gì vậy thầy nhỉ? e thử debug thì không thấy code tại mục đó được sử dụng. còn left, top, right, height thì thấy có.
Mã:
Public Sub Wrap(ByVal ctrlCoomandButton As Object, Optional ByVal strCaption As String = "")
    Set ctrl = ctrlCoomandButton
    If strCaption <> "" Then
        ctrl.Caption = strCaption
    End If
End Sub
code này để gán top cho đối tượng hay sao thầy
Mã:
Public Property Let Top(ByVal vData As Integer): ctrl.Top = vData: End Property
còn code này trong form lấy top của đối tượng?
Mã:
Public Property Get Top() As Integer: Top = ctrl.Top: End Property

vụ class về đối tượng e chỉ mò được vài hôm nay chứ không biết nhiều
thầy có file mẫu hay gì thì cho e xin
em có xem file mẫu của anh thaipv bên bài này thì thấy thích kiểu giao diện như vậy?
nếu sử dụng giao diện bằng class như thế liệu có ảnh hưởng đến việc sử lý trong một dự án lớn không?
 
Upvote 0
VBA không phải là ngôn ngữ hướng đối tượng cho nên Class Module của nó không đạt đủ tiêu chuẩn hướng đối tượng. VBA không cho cách nào để tạo thuộc tính và phương thức riêng của class.

Tuy nhiên, trường hợp cần "chia sẻ" một array thì có thể tạm dùng phương pháp đi vòng:
- tạo một class module, ví dụ là X, với thuộc tính là cái array sẽ chia sẻ.
- trong các class modules cần làm việc với array trên. Tạo một thuộc tính có kiểu là Object
- lúc dùng, dựng một đối tượng với kiểu X. Và nạp dữ liệu nếu cần.
- các đối tượng của các class modules kia, lúc cần đến array thì gọi một phương thức KetNoiArray, Set thuộc tính đã định trên cho nó tham chiếu đến đối tượng đã dựng của kiểu X.

Lý thuyết: VBA không có con trỏ hay cách tham chiếu trực tiếp. Cách duy nhất để nhiều biến có thể cùng tham chiếu đến một trị là qua đối tượng.
 
Upvote 0
VBA không phải là ngôn ngữ hướng đối tượng cho nên Class Module của nó không đạt đủ tiêu chuẩn hướng đối tượng. VBA không cho cách nào để tạo thuộc tính và phương thức riêng của class.

Tuy nhiên, trường hợp cần "chia sẻ" một array thì có thể tạm dùng phương pháp đi vòng:
- tạo một class module, ví dụ là X, với thuộc tính là cái array sẽ chia sẻ.
- trong các class modules cần làm việc với array trên. Tạo một thuộc tính có kiểu là Object
- lúc dùng, dựng một đối tượng với kiểu X. Và nạp dữ liệu nếu cần.
- các đối tượng của các class modules kia, lúc cần đến array thì gọi một phương thức KetNoiArray, Set thuộc tính đã định trên cho nó tham chiếu đến đối tượng đã dựng của kiểu X.

Lý thuyết: VBA không có con trỏ hay cách tham chiếu trực tiếp. Cách duy nhất để nhiều biến có thể cùng tham chiếu đến một trị là qua đối tượng.
vâng e cảm ơn nhiều ạ, vậy chắc chỉ có cách này, theo file bên trên thì e truyền cho mỗi Ctrl 1 mảng Arr y chang (như vậy sẽ chiếm nhiều bộ nhớ nếu mảng lớn), em cũng không biết viết class, cái này e xem code mẫu rồi sửa lại, với cho em hỏi thêm nếu áp dụng cái list giả này vào một form xử lý nhiều dữ liệu thì có làm treo hay xử lý chậm không? nếu có mà ko có cách khắc phục thì thôi chắc em dừng lại không làm tiếp nữa.
còn nếu phát triển thêm chắc làm được thêm cái vụ thay đổi chiều rộng cột, còn lăn chuột chắc thua.
 
Upvote 0
trong lúc vận hành thì có truyền vào mảng Arr của listbox như trong hình
Nếu vậy thì nó thêm mảng theo từng ctrl, vào lúc thêm như vậy thì ở ctrl khác cũng phải thêm, như vậy là mỗi ctrl đều có mảng riêng , vậy làm sao để chỉ thêm mảng 1 lần để dùng chung cho class đó được
đó là ở vấn đề mảng cố định, còn chưa tính đến lọc cái mảng đó thì làm sao để thay đổi được ạ
tóm lại là thầy có thể gợi ý hoặc giúp em làm sao để việc truyền mảng vào linh hoạt được không ạ,
Tôi chỉ muốn tham gia 1 lần thôi. Vấn đề của bạn mà tham gia tới cùng thì chỉ còn nước viết cho bạn từ A tới Z.

Bạn dùng class clsCommandButton thực ra chỉ để tạo đối tượng và phục vụ cho mỗi Label thôi. Và cứ 4 Label thì đại diện cho 1 dòng. Nếu bạn muốn tạo ListBox thì nên có thêm vd. class clsMyListBox. Class này có vd. trường dulieu, và có phương thức vd. Create, với ít nhất 2 parameter: Owner - chủ nhân của ListBox, và Arr - mảng dữ liệu mà từ đó tạo các dòng cột của ListBox.

Mỗi khi cần tạo ListBox thì tạo đối tượng class clsMyListBox -> gọi phương thức Create của nó. Create sẽ lưu mảng Arr trong trường dulieu, Owner lưu trong vd. OwnerObj và tiếp theo gọi CreateListBox với tham số là mảng dulieu -> CreateListBox duyệt từng dòng của dulieu (tham số) -> với mỗi dòng duyệt từng cột của dulieu -> mỗi lần duyệt từng cột của dulieu trong mỗi lần duyệt dòng thì tạo đối tượng clsCommandButton -> thiết lập Caption của Label bằng giá trị dulieu(r, c) -> định vị Label dựa vào r và c -> những Label được tạo sẽ có Set ctrl = OwnerObj.Controls.Add("Forms.Label.1"). Tùy theo nhu cầu mà truyền Owner = Form hoặc Owner = Frame khi gọi Create. Tóm lại phương thức CreateListBox sẽ tạo UBound(dulieu, 1) hàng Label, mỗi hàng Label có UBound(dulieu, 2) Label, nằm trong Owner, mỗi Label có Caption = dulieu(r, c).

clsCommandButton chỉ để phục vụ mọi cái liên quan tới Label thôi. Tất nhiên đã sinh ra clsMyListBox thì phải sửa clsCommandButton. clsMyListBox thì phục vụ mọi cái liên quan tới ListBox.

Code chính sẽ tạo clsMyListBox và gọi phương thức Create của nó. Create lưu Arr trong trường dulieu và gọi phương thức CreateListBox với tham số dulieu. Chính CreateListBox mới tạo các đối tượng clsCommandButton ...

Lọc có thể như sau: khi bắt đầu thì hủy các Label -> lọc -> kết quả lọc trong mảng vd. dulieu_loc (có thể là 1 trường của clsMyListBox). Nếu dulieu_loc có dữ liệu thì gọi CreateListBox và truyền mảng dulieu_loc để tạo ListBox mới tương ứng với mảng lọc.

Không có chuyện Arr nằm trong Module với tư cách Public. Mảng nằm trong class clsMyListBox và được truyền vào 1 lần ở Create. Nếu có nhu cầu thay đổi mảng cho ListBox trong quá trình hoạt động của ListBox thì tốt nhất tạo thuộc tính read/write ListBox cho clsMyListBox. Set sẽ lưu Arr vào dulieu và gọi CreateListBox với tham số dulieu để tạo mới ListBox. Get thì trả về mảng dulieu. Lúc đó thì sau khi tạo đối tượng clsMyListBox thì dùng SET để thiết lập thuộc tính ListBox của nó.

Đại loại mọi cái là như trên. Mọi dữ liệu nằm gói gọn trong các class, không có chuyện nằm ở ngoài sheet, module.
 
Upvote 0
Tôi chỉ muốn tham gia 1 lần thôi. Vấn đề của bạn mà tham gia tới cùng thì chỉ còn nước viết cho bạn từ A tới Z.

Bạn dùng class clsCommandButton thực ra chỉ để tạo đối tượng và phục vụ cho mỗi Label thôi. Và cứ 4 Label thì đại diện cho 1 dòng. Nếu bạn muốn tạo ListBox thì nên có thêm vd. class clsMyListBox. Class này có vd. trường dulieu, và có phương thức vd. Create, với ít nhất 2 parameter: Owner - chủ nhân của ListBox, và Arr - mảng dữ liệu mà từ đó tạo các dòng cột của ListBox.

Mỗi khi cần tạo ListBox thì tạo đối tượng class clsMyListBox -> gọi phương thức Create của nó. Create sẽ lưu mảng Arr trong trường dulieu, Owner lưu trong vd. OwnerObj và tiếp theo gọi CreateListBox với tham số là mảng dulieu -> CreateListBox duyệt từng dòng của dulieu (tham số) -> với mỗi dòng duyệt từng cột của dulieu -> mỗi lần duyệt từng cột của dulieu trong mỗi lần duyệt dòng thì tạo đối tượng clsCommandButton -> thiết lập Caption của Label bằng giá trị dulieu(r, c) -> định vị Label dựa vào r và c -> những Label được tạo sẽ có Set ctrl = OwnerObj.Controls.Add("Forms.Label.1"). Tùy theo nhu cầu mà truyền Owner = Form hoặc Owner = Frame khi gọi Create. Tóm lại phương thức CreateListBox sẽ tạo UBound(dulieu, 1) hàng Label, mỗi hàng Label có UBound(dulieu, 2) Label, nằm trong Owner, mỗi Label có Caption = dulieu(r, c).

clsCommandButton chỉ để phục vụ mọi cái liên quan tới Label thôi. Tất nhiên đã sinh ra clsMyListBox thì phải sửa clsCommandButton. clsMyListBox thì phục vụ mọi cái liên quan tới ListBox.

Code chính sẽ tạo clsMyListBox và gọi phương thức Create của nó. Create lưu Arr trong trường dulieu và gọi phương thức CreateListBox với tham số dulieu. Chính CreateListBox mới tạo các đối tượng clsCommandButton ...

Lọc có thể như sau: khi bắt đầu thì hủy các Label -> lọc -> kết quả lọc trong mảng vd. dulieu_loc (có thể là 1 trường của clsMyListBox). Nếu dulieu_loc có dữ liệu thì gọi CreateListBox và truyền mảng dulieu_loc để tạo ListBox mới tương ứng với mảng lọc.

Không có chuyện Arr nằm trong Module với tư cách Public. Mảng nằm trong class clsMyListBox và được truyền vào 1 lần ở Create. Nếu có nhu cầu thay đổi mảng cho ListBox trong quá trình hoạt động của ListBox thì tốt nhất tạo thuộc tính read/write ListBox cho clsMyListBox. Set sẽ lưu Arr vào dulieu và gọi CreateListBox với tham số dulieu để tạo mới ListBox. Get thì trả về mảng dulieu. Lúc đó thì sau khi tạo đối tượng clsMyListBox thì dùng SET để thiết lập thuộc tính ListBox của nó.

Đại loại mọi cái là như trên. Mọi dữ liệu nằm gói gọn trong các class, không có chuyện nằm ở ngoài sheet, module.
vâng em cảm ơn ạ, nếu đây là các bước để nó hoạt động thì em nghiêm cứu xem sao. chứ vụ class em mới đụng vô 1 tuần nay, hoàn toàn không có căn bản gì về class hết ạ.
 
Upvote 0
Web KT
Back
Top Bottom