TreeView

tuhocvba

Administrator
Thành viên BQT
TreeView là một biểu diễn rất trực quan. Tuy nhiên nó chỉ dùng được cho office 32bit. Bản office 64bit là không dùng được.
Tuy nhiên dự án dưới đây sẽ dùng TreeView cho cả Office 64bit.
Bạn cần đăng nhập để thấy hình ảnh

Cách nghĩ là, làm thế nào để bất cứ ai cũng đều dùng được TreeView. Cho nên dự án này ra đời.
File download:
Nguồn:

Cũng tương tự như các dự án trước, tôi muốn các bạn tìm hiểu được đến đâu thì chia sẻ đến đó. Đây là dự án có tính áp dụng cao, chừng nào Microsoft còn chưa cập nhật TreeView cho Office 64bit, thì vấn đề này vẫn là quan trọng.
Tôi đặt nhiều kỳ vọng vào các bạn như @NhanSu , @thaipv , @vothanhthu , @ducdoom ...
Mong nhận được trợ giúp của các bạn.
 

tuhocvba

Administrator
Thành viên BQT
Trong file có rất nhiều sheet. Trong các sheet đều có thuyết minh (tiếng anh). Vì vậy, các thành viên có thêm lợi thế tìm hiểu. File này họ làm chuyên nghiệp quá.
 

BKKBG

Yêu THVBA nhất
Đầu tiên tôi tìm hiểu về nguyên liệu họ làm, thời gian không có nhiều, tôi tranh thủ tìm hiểu nên kết quả cũng còn khiêm tốn.
Họ dùng các đối tượng sau để biểu diễn hình một TreeView:
  • Frame
  • Label (tôi nghĩ họ dùng Label để biểu diễn đường kẻ, tôi chưa tìm hiểu kỹ)
  • Textbox (nhờ có cái này nên mới edit được tên Node)
  • Image (tôi nghĩ họ dùng Image để biểu diễn icon cho các Node)
  • Checkbox (mọi người có thể tham khảo bên trong các class, xem các thuộc tính có dạng Property Set )
 
B

bvtvba

Guest
Mình xin làm rõ các đánh giá của bạn BKKBG:
Mình cũng tò mò xem họ lấy cái khung cho Tree View ở đâu. Thật tiếc là họ đã đổi tên đối tượng thành frTreeControl. Mình tò mò không biết dựa vào đâu mà bạn BKKBG biết đó là frame.
Bạn cần đăng nhập để thấy hình ảnh

-Mình xóa toàn bộ các đối tượng trên UserForm đi.
-Bên trong UserForm, tìm tới thủ tục này, xóa hết nội dung bên trong và chặn ở đầu thủ tục.
Bạn cần đăng nhập để thấy hình ảnh

Chạy Play cho UserForm hiện lên.
Đúng là frTreeControl là Frame thật rồi.
Bạn cần đăng nhập để thấy hình ảnh

Mình ghi chú lại các thủ tục chính tạo nên hình TreeView, sẽ tìm hiểu tiếp:

InitializeDemo1
BuildRoot
Round75
HLine
 

NhanSu

SMod
Thành viên BQT
Chương trình này lớn quá admin ạ, nhìn hoa hết mắt, phải có thời gian. @bvtvba frame ở
 
Sửa lần cuối:
B

bvtvba

Guest
Chương trình này lớn quá admin ạ, nhìn hoa hết mắt, phải có thời gian. @bvtvba frame ở đây này
Ặc ặc, mình thấy rồi. Cảm ơn bạn. Mà bạn xem hướng dẫn post ảnh ở đây này:
 
V

vothanhthu

Guest
Thứ cung xin đóng góp chút ít

Cấu trúc chương trình (Theo thứ tự):
InitializeDemo1: Sub đầu tiên khởi nguồn cho mọi thứ trên đời. Sub khai báo một loạt các tinh chỉnh Root, các Keys. Ngay sau đó Sub sẽ dẫn đến BuildRoot.

BuildRoot
: Đây là Sub sẽ tạo ra thanh Keys gốc đầu tiên.
Bạn cần đăng nhập để thấy hình ảnh

Sau khi tạo xong nó sẽ dẫn đến dòng Code để gọi BuildTree :
Mã:
'Now add this root's children
If Not cRoot.ChildNodes Is Nothing Then
   BuildTree cRoot, 1, True
End If
BuildTree: Sub sẽ đóng vai trò tạo ra các cấp độ con của Keys gốc.
Ví dụ trong đó, đây là đoạn Code tạo ra thanh xuống của Treeview trong Sub BuildTree
Mã:
'Vertical line
        If mbShowLines Then
            If cNode.VLine Is Nothing Then
                Set cNode.VLine = TreeControl.Controls.Add("Forms.label.1", "", False)
                lVLineTopIdx = mlVisCount
                With cNode.VLine
                    .Tag = "VLine"
                    .ZOrder 1
                    .Top = msngTopHV + (lVLineTopIdx - 1) * NodeHeight
                    .Left = msngLineLeft + msngRootLine + msngIndent * (lLevel - 1)
                    .Width = 0.75
                    .Height = NodeHeight
                    .Caption = ""
                    .BorderColor = vbScrollBars
                    .BorderStyle = fmBorderStyleSingle
                    .Visible = True
                End With

            Else
                lVLineTopIdx = mlVisCount
                cNode.VLine.Top = msngTopHV + (lVLineTopIdx - 1) * NodeHeight
                If mbRedesign Then
                    cNode.VLine.Left = msngLineLeft + msngRootLine + msngIndent * (lLevel - 1)
                End If
            End If
        End If
Bạn cần đăng nhập để thấy hình ảnh


Sau khi tạo thanh xuống, Sub BuildNodeControls sẽ được gọi.
Mã:
BuildNodeControls cChild, lLevel
BuildNodeControls: Đây là đoạn code tạo thanh ngang trong Sub BuildNodeControls
Mã:
'Horizontal line
    If mbShowLines Then
        If cNode.HLine Is Nothing Then
            Set cNode.HLine = TreeControl.Controls.Add("Forms.label.1", "", False)
            With cNode.HLine
                .Tag = "HLine"
                .Left = msngLineLeft + msngRootLine + msngIndent * (lLevel - 1)
                .Top = msngTopHV + mlVisCount * NodeHeight
                .Width = msngIndent
                .Height = 0.75
                .Caption = ""
                .BorderStyle = fmBorderStyleSingle
                .BorderColor = vbScrollBars
                .Visible = True
            End With
        Else
            With cNode.HLine
                If mbRedesign Then
                    .Left = msngLineLeft + msngRootLine + msngIndent * (lLevel - 1)
                    .Width = msngIndent
                End If
                .Top = msngTopHV + mlVisCount * NodeHeight
                .Visible = True
            End With

        End If
    End If
Bạn cần đăng nhập để thấy hình ảnh


Đây là đoạn code dùng để tạo tiêu đề trong sub BuildNodeControls
Mã:
'The node itself
    If cNode.Control Is Nothing Then

        Set cNode.Control = TreeControl.Controls.Add("Forms.label.1", "", False)
        With cNode.Control
            .Tag = "NodeLabel"
            .WordWrap = False
            .AutoSize = True
            .Left = 3 + msngRootLine + msngIndent * lLevel + msngChkBoxPad + sngIconPad
            .Top = 3 + msngTopLabel + mlVisCount * NodeHeight

            If Not mbFullWidth And mbGotIcons Then
                If cNode.hasIcon(vKey) Then
                    .PicturePosition = fmPicturePositionLeftCenter
                    .Picture = mcolIcons(vKey)
                End If
            End If

            If cNode.Bold Then .Font.Bold = True
            .WordWrap = False
            .AutoSize = True
            .Caption = cNode.Caption
            cNode.TextWidth = .Width

            If cNode.TextWidth + sngIconPad > msngMaxWidths(lLevel) Then
                msngMaxWidths(lLevel) = cNode.TextWidth + sngIconPad
            End If

            If mbFullWidth Then
                .AutoSize = False
                If .Width <= 600 Then .Width = 600
            End If
            If cNode.BackColor Then
                .BackColor = cNode.BackColor
            End If
            If cNode.ForeColor Then
                .ForeColor = cNode.ForeColor
            End If
            .Visible = True
        End With

        mlNodesCreated = mlNodesCreated + 1

    Else
        With cNode.Control
            If mbRedesign Then
                .Left = 3 + msngRootLine + msngIndent * lLevel + sngIconPad + msngChkBoxPad
                cNode.TextWidth = .Width
                If .Width + sngIconPad > msngMaxWidths(0) Then
                    msngMaxWidths(lLevel) = .Width + sngIconPad
                End If
            End If

            .Top = mlVisCount * NodeHeight + 3 + msngTopLabel
            .Visible = True
        End With
    End If
Bạn cần đăng nhập để thấy hình ảnh

Sau khi tạo xong sẽ quay trả ta lại Sub BuildTree

BuildTree
:
Đây là đoạn Code sẽ tạo thanh sổ xuống
Mã:
'Expand/collapse button text (or icon)
        If cNode.Expander Is Nothing Then
            Set cNode.Expander = TreeControl.Controls.Add("Forms.label.1", False)
            With cNode.Expander
                .Tag = "ExpText"
                .Left = 6 + (lLevel - 2) * msngIndent + msngRootLine
                .Top = (mlVisCount - 1) * NodeHeight + msngTopExpT

                If mbExpanderImage Then
                    'Use an image
                    .BorderStyle = fmBorderStyleNone
                    .Picture = moExpanderImage(cNode.Expanded)
                    .PicturePosition = fmPicturePositionLeftTop
                    .Width = 7.5
                    .Height = 7.5
                Else
                    'Plus or minus
                    .Width = 11.25
                    .Height = 11.25

                    If cNode.Expanded = True Then
                        .Caption = "-"
                        .Font.Bold = True
                    Else
                        .Caption = "+"
                        .Font.Bold = False
                    End If

                    .Font.Size = 10
                    .TextAlign = fmTextAlignCenter
                    .BackStyle = fmBackStyleTransparent
                    .Visible = True
                End If
            End With
        Else
            With cNode.Expander
                If mbRedesign Then .Left = 6 + (lLevel - 2) * msngIndent + msngRootLine
                .Top = (mlVisCount - 1) * NodeHeight + msngTopExpT
                .Visible = True
            End With
        End If
Bạn cần đăng nhập để thấy hình ảnh

Cuối Code BuildTree, nếu cấp độ tiếp theo bằng cấp độ hiện tại sẽ tạo nút sổ ngang:
Mã:
' extend the vertical line
            If mbShowLines Then
                With cNode.VLine
                    .Height = (mlVisCount - lVLineTopIdx + 1) * msngNodeHeight
                    .Visible = True
                End With
            End If
Bạn cần đăng nhập để thấy hình ảnh

Đoạn Code này tăng dần cấp độ Keys đến cấp độ Keys cuối cùng còn sót lại của 1 nhánh
Mã:
If Not cChild.ChildNodes Is Nothing Then
      BuildTree cChild, lLevel + 1
End If
Có đoạn Code này Thứ chưa hiểu lắm, Tại Sub BuildTree, Cuối code, tại sao khi code chạy đến Exit sub rồi thì nó lại quay ngược lại End if?. Nó đã đi qua vòng lặp rồi cơ mà, cơ chế nào làm cho nó quay ngược lại?


Bạn cần đăng nhập để thấy hình ảnh

Trước mắt, Thứ chỉ biết bấy nhiều, Code dài quá đi mất...!
 
Sửa lần cuối bởi điều hành viên:

NhanSu

SMod
Thành viên BQT
@vothanhthu ở hình cuối bạn đang bấm F8 để chạy từng lệnh hay chỉ đặt breakpoint rồi bấm Run? Mình nghĩ bạn đang đặt breakponit rồi bấm Run (F5), do đây là thủ tục đệ quy, được gọi nhiều lần nên đến Exit sub rồi nó lại chạy tiếp thủ tục lần sau.
 
V

vothanhthu

Guest
@NhanSu Như hình bạn thấy, Next là điểm breakponit mình đặt, sau đó nhấn F8 lần lần, vừa sau Exit sub là nó nhảy về thẳng về End if (chổ mũi tên). Đệ quy như thể nào nhỉ, Thứ chưa hiểu lắm...?. Bạn hiểu giải thích Thứ với !
 

tuhocvba

Administrator
Thành viên BQT
@vothanhthu trình bày khá dễ hiểu. Cám ơn Thứ nhiều.
@NhanSu : Trình bày rõ hơn để mọi người hiểu thì tốt. Tôi hiểu ý bạn định nói nhưng nên mô hình hóa để mọi người hiểu, không võ đoán. Nhìn cách diễn đạt của Thứ ở trên, diễn đạt như vậy là được.
 

NhanSu

SMod
Thành viên BQT
@vothanhthu đoạn này trong method BuildTree
Mã:
If Not cChild.ChildNodes Is Nothing Then           '(1)
                BuildTree cChild, lLevel + 1                   '(2)
            End If                                                         '(3)

        Next

    End If    ' cNode.Expanded And (lMaxLevel < lLevel Or lMaxLevel = -1)

    Exit Sub
Node cChild là con của cNode, nếu cChild lại có node con (tức là node cháu của cNode) thì lệnh (2) sẽ gọi lại method BuildTree. Việc một thủ tục hay hàm gọi chính nó gọi là đệ quy. có nói về đệ quy.
Ở trường hợp cụ thể này, ta tạm quy ước thủ tục BuildTree1 gọi thủ tục BuildTree2 . Khi BuildTree1 chạy đến lệnh (1), nó gọi BuildTree2. BuildTree2 chạy từ đầu đến Exit sub thì thoát, khi đó con trỏ lệnh lại quay về lệnh (3) của BuildTree1.
@tuhocvba đúng là có hình vẽ thì dễ hiểu hơn để hiểu về đệ quy, nhưng thú thật là mấy khoản vẽ hình mình rất kém.
 
Sửa lần cuối:
V

vothanhthu

Guest
Ra là vậy, Cảm ơn @NhanSu nhiều nha
nhưng thú thật là mấy khoản vẽ hình mình rất kém.
Vậy, Thứ xin phép vẽ lại hình theo nội dung ở trên về Thủ tục đệ quy nhé!
Mũi tên xanh: lần đi đầu tiên
Mũi tên đỏ: lần đi thứ 2


Bạn cần đăng nhập để thấy hình ảnh
 
Sửa lần cuối bởi điều hành viên:

Euler

Administrator
Thành viên BQT
Cảm ơn @vothanhthu đã có phân tích rất dễ hiểu.
Dưới đây mình xin được tái hiện việc kẻ đường kẻ ngang trong frame.
Bạn cần đăng nhập để thấy hình ảnh


Có hai thông số quan trọng để tạo thành một đường kẻ: Đó là Height, độ cao của Label phải đủ hẹp, nếu không thì nó sẽ có dạng hình chữ nhật.
Qua điều tra code ở trên thì Height = 0.75
Bạn cần đăng nhập để thấy hình ảnh

Thông số thứ hai là màu cho nhãn.

Vậy bây giờ em xin phép tái hiện kẻ đường kẻ ngang trên Frame như sau:
Em tạo Frame1 trên UserForm:
Bạn cần đăng nhập để thấy hình ảnh


Trên đó viết code như sau:
Mã:
Private Sub UserForm_Initialize()
    'Frame1.Controls.Add("Forms.label.1", "HLine" & sName, False)
    Dim o1  As Object
    
    Set o1 = Frame1.Controls.Add("Forms.label.1", "tuhocvba", False)
    With o1
      
                .Left = 5
                .Top = 10
                .Width = 50
                .Height = 0.75
                .Caption = ""
                .BorderStyle = fmBorderStyleSingle
                .BorderColor = vbScrollBars 'vbScrollBars
'                 If mbRedesign Then
'                    .ZOrder 1
'                 End If
                .Visible = True
      
    End With
End Sub
Các dòng code 15-17, mình chưa hiểu. Nếu ai hiểu thì giải thích giúp mình nhé. Tạm thời mình commentout.
Bây giờ xem thành quả:
Bạn cần đăng nhập để thấy hình ảnh


Chúng ta đã tạo đường kẻ ngang thành công.
 

vbano1

SMod
Thành viên BQT
Các dòng code 15-17, mình chưa hiểu. Nếu ai hiểu thì giải thích giúp mình nhé. Tạm thời mình commentout.
Bạn cần đăng nhập để thấy hình ảnh

Nhìn vào điều kiện logic thì có nghĩa là nếu Frame có số lượng Object > 0 (Object: label, checkbox,...) thì điều kiện logic thỏa mãn gán là True.
Vì vậy code của @Euler trong trường hợp tái hiện đường kẻ ngang có thể viết là:
Mã:
Private Sub UserForm_Initialize()
    'Frame1.Controls.Add("Forms.label.1", "HLine" & sName, False)
    Dim o1  As Object
    
    Set o1 = Frame1.Controls.Add("Forms.label.1", "tuhocvba", False)
    With o1
      
                .Left = 5
                .Top = 10
                .Width = 50
                .Height = 0.75
                .Caption = ""
                .BorderStyle = fmBorderStyleSingle
                .BorderColor = vbScrollBars 'vbScrollBars
                 If 1 Then
                    .ZOrder 1
                 End If
                .Visible = True
      
    End With
End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh

Mình không hiểu thuộc tính ZOrder của Frame có ý nghĩa gì. @vothanhthu có biết không?
Mình tìm được link này nhưng đọc không hiểu:
 

tuhocvba

Administrator
Thành viên BQT
Cảm ơn mọi người. Về ZOrder, đây là trình tự hiển thị ra phía trước.
Bạn cần đăng nhập để thấy hình ảnh

Ví dụ, ở trên Frame2 đang đè lên trên Frame1.
Khi cho hiển thị, ta thấy Frame2 đang che khuất 1 góc của Frame1.
Bạn cần đăng nhập để thấy hình ảnh


Nhưng bây giờ tôi viết code như sau:
Mã:
Private Sub UserForm_Initialize()
    Frame2.ZOrder 1
End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh


Tổng kết:
Bạn cần đăng nhập để thấy hình ảnh

Nguồn tham khảo:
 

Euler

Administrator
Thành viên BQT
Tiếp theo mọi người cùng mình tìm hiểu các kích thước dưới đây nhé:
Bạn cần đăng nhập để thấy hình ảnh
 
V

vothanhthu

Guest
Dựa vào cấu trúc tại #10 và dữ liệu từ #17. Chúng ta có thể tạo được tiêu đề cho Frame bằng đoạn Code sau:
Mã:
        Set o2 = Frame1.Controls.Add("Forms.label.1", sName, False)
        With o2
            .WordWrap = False
            .Left = 34 'THUC LE TRAI
            .Top = 10 'THUC LE TREN
            .Font.Bold = True 'IN DAM
            .AutoSize = True 'AUTOSIZE
            .Caption = "Tuhocvba.net" 'TEN TIEU DE
            .Width = 100 'CHIEU DAI CUA HOP THOAI CHUA TIEU DE
            .ControlTipText = ""
        .Visible = True
        End With
Đây là đoạn code tạo nên đấu + và - kia:
Mã:
    Set o3 = Frame1.Controls.Add("Forms.label.1", "Tuhocvba3", False)
            With o3
                    .Left = 20 'LE TRAI
                    .Top = 8 'LE TREN
                    .Width = 11.5 'DAI
                    .Height = 11.5 'RONG

                  '  If cNode.Expanded = True Then
                  '      .Caption = "-"
                  '      .Font.Bold = True
                  '  Else
                        .Caption = "+" 'TEN 
                        .Font.Bold = False 'KHONG IN DAM
                  '  End If

                    .Font.Size = 10 'KICH THUOC FONT
                    .TextAlign = 2 
                    .BackStyle = 0
                .Visible = True
            End With
Đây là kết quả
Bạn cần đăng nhập để thấy đính kèm
 

BKKBG

Yêu THVBA nhất
Tiếp theo mọi người cùng mình tìm hiểu các kích thước dưới đây nhé:
Bạn cần đăng nhập để thấy hình ảnh
Về khoảng cách màu xanh, tôi nghĩ nó liên quan tới thuộc tính .Top
Xem lại code ở : Phần code tạo thanh ngang:
Mã:
'Horizontal line
    If mbShowLines Then
        If cNode.HLine Is Nothing Then
            Set cNode.HLine = TreeControl.Controls.Add("Forms.label.1", "", False)
            With cNode.HLine
                .Tag = "HLine"
                .Left = msngLineLeft + msngRootLine + msngIndent * (lLevel - 1)
                .Top = msngTopHV + mlVisCount * NodeHeight
                .Width = msngIndent
                .Height = 0.75
                .Caption = ""
                .BorderStyle = fmBorderStyleSingle
                .BorderColor = vbScrollBars
                .Visible = True
            End With
        Else
            With cNode.HLine
                If mbRedesign Then
                    .Left = msngLineLeft + msngRootLine + msngIndent * (lLevel - 1)
                    .Width = msngIndent
                End If
                .Top = msngTopHV + mlVisCount * NodeHeight
                .Visible = True
            End With

        End If
    End If
Tôi phân tích được như sau:
Bạn cần đăng nhập để thấy hình ảnh


Liên quan tới hằng số msngNodeHeight , cái này rất phức tạp, tôi chưa hiểu logic. Ai hiểu xin hãy chỉ giáo.
Bạn cần đăng nhập để thấy hình ảnh
 
Top