ListView: Làm sao có thể check nhiều trong 1 lần thao tác. (5 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,662
Được thích
16,722
Giới tính
Nam
Giống như trong các listview của mail hay các chương trình khác, thì hộp kiểm (checkbox) trên listview, người ta chỉ việc check ở mục đầu rồi sau đó bấm giữ phím Shift và check tiếp ở mục khác, thì khoảng giữa từ 2 mục check đầu và cuối đều được check.

Vậy cho hỏi, với listview trong excel, ta phải làm như thế nào mới thao tác được như vậy?

Xin cám ơn.
 

File đính kèm

Lần chỉnh sửa cuối:
Thử thế này nhé

[GPECODE=vb]Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
'Nhấp chuột vào cột tiêu đề sẽ chọn hoặc bỏ chọn hết các mục.
ColumnHeader.Tag = Not CBool(IIf(ColumnHeader.Tag = "", 0, ColumnHeader.Tag))
Dim i As Long
For i = 1 To ListView1.ListItems.Count
ListView1.ListItems(i).Checked = CBool(ColumnHeader.Tag)
Next i
End Sub

Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
'Item.Checked = Item.Selected
End Sub

Private Sub UserForm_Initialize()
Dim i As Long
With ListView1
.MultiSelect = True 'Cho phep chon nhieu iem
.ColumnHeaders.Add , , "VALUE", .Width - 4
.CheckBoxes = True
For i = 1 To 10
.ListItems.Add , , "Item" & i
Next
End With
End Sub
[/GPECODE]
 
Upvote 0
Thử thế này nhé

[GPECODE=vb]Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
'Nhấp chuột vào cột tiêu đề sẽ chọn hoặc bỏ chọn hết các mục.
ColumnHeader.Tag = Not CBool(IIf(ColumnHeader.Tag = "", 0, ColumnHeader.Tag))
Dim i As Long
For i = 1 To ListView1.ListItems.Count
ListView1.ListItems(i).Checked = CBool(ColumnHeader.Tag)
Next i
End Sub

Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
'Item.Checked = Item.Selected
End Sub

Private Sub UserForm_Initialize()
Dim i As Long
With ListView1
.MultiSelect = True 'Cho phep chon nhieu iem
.ColumnHeaders.Add , , "VALUE", .Width - 4
.CheckBoxes = True
For i = 1 To 10
.ListItems.Add , , "Item" & i
Next
End With
End Sub
[/GPECODE]

Hình như Anh Tuân chưa hiểu ý em, em chỉ cần check vào một khu vực nào đó thôi chứ không phải check tất cả. Ví dụ có 10 mục, em chỉ cần chọn từ mục 3 đến mục 8 thôi.
 
Upvote 0
Nếu mà muốn multi select rồi thì bỏ thuộc tính check đi. Khi đó ứng dụng sẽ làm việc với những item.Selected = True.
 
Upvote 0
Nếu mà muốn multi select rồi thì bỏ thuộc tính check đi. Khi đó ứng dụng sẽ làm việc với những item.Selected = True.

Tại thấy trong Yahoo Mail có cái listview trong các hộp thư có checkbox đặc biệt nên mới thử làm, nhưng không được như họ!

[video=youtube;igtTWvlz9mU]http://www.youtube.com/watch?v=igtTWvlz9mU&feature=youtu.be[/video]
 
Upvote 0
Tại thấy trong Yahoo Mail có cái listview trong các hộp thư có checkbox đặc biệt nên mới thử làm, nhưng không được như họ!

Như thế này không biết đúng ý chưa? %#^#$

[GPECODE=vb]Option Explicit

Private Sub UserForm_Initialize()
Dim I As Long
With ListView1
.MultiSelect = True 'Cho phep chon nhieu item va chay CheckItemsWithSelectState trong ListView1_ItemClick
.ColumnHeaders.Add , , "VALUE", .Width - 4
.CheckBoxes = True
For I = 1 To 10
.ListItems.Add , , "Item" & I
Next
End With
End Sub

Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
ColumnHeader.Tag = Not CBool(IIf(ColumnHeader.Tag = "", 0, ColumnHeader.Tag))
CheckItems CBool(ColumnHeader.Tag)
End Sub

Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
If ListView1.MultiSelect Then
CheckItemsWithSelectState
End If
End Sub

Private Function CheckItems(ByVal bCheck As Boolean) As Long
Dim I As Long
For I = 1 To ListView1.ListItems.Count
CheckItems = CheckItems + 1
ListView1.ListItems(I).Checked = bCheck
Next I
End Function

Private Function CheckItemsWithSelectState() As Long
Dim I As Long
For I = 1 To ListView1.ListItems.Count
CheckItemsWithSelectState = CheckItemsWithSelectState + 1
ListView1.ListItems(I).Checked = ListView1.ListItems(I).Selected
Next I
End Function
[/GPECODE]
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Một phát hiện đặc biệt là Select1 + Shift + Select2 thì checked tất cả không vòng lặp

Khi load form, phải có 2 dòng màu đỏ:

Mã:
Private Sub UserForm_Initialize()
    Dim i As Long
    With ListView1
        [COLOR=#ff0000][B].MultiSelect = True[/B][/COLOR]
        .ColumnHeaders.Add , , "VALUE", .Width - 4
        [COLOR=#ff0000][B].CheckBoxes = True[/B][/COLOR]
        For i = 1 To 10000
            .ListItems.Add , , "Item" & i
        Next
    End With
End Sub

Khi sự kiện Click vào Item (cột đầu tiên) với thủ tục:

Mã:
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    ListView1.ListItems(Item.Index).Checked = True
End Sub

Thì tất cả các hàng được select sẽ được checked.
 
Upvote 0
Và như thế để xác định cho cột tiêu đề checked hay unchecked mình phải có 2 biến để ở ngoài:

Mã:
Private IsCheck As Boolean, Idx As Long

Private Sub UserForm_Initialize()
    Dim i As Long
   [COLOR=#ff0000][B] IsCheck = True[/B][/COLOR]
    With ListView1
        .MultiSelect = True
        .ColumnHeaders.Add , , "ITEMS", 100
        .ColumnHeaders.Add , , "SUBITEMS", 100
        .CheckBoxes = True
        For i = 1 To 10000
            With .ListItems.Add(, , "Item" & i)
                .SubItems(1) = "SubItem" & i
            End With
        Next
    End With
End Sub

Với IsCheck nhằm xác định Item đã được check hay chưa và Idx nhằm xác định ListItem Index lớn nhất để chạy vòng lặp ngắn với dữ liệu lớn.

Các thủ tục để xác định Check hay UnCheck:

Mã:
Private Sub ListView1_ItemCheck(ByVal Item As MSComctlLib.ListItem)
    ListView1.ListItems(Item.Index).Selected = ListView1.ListItems(Item.Index).Checked
    '---------------------------------------------------
    'Thu tuc duoi day de phuc vu cho su kien ColumnClick:
    If Item.Checked Then
        Idx = IIf(Idx > Item.Index, Idx, Item.Index)
        IsCheck = False
    Else
        IsCheck = True
        If Idx = 0 Then Exit Sub
        For i = 1 To Idx
            If ListView1.ListItems(i).Checked Then IsCheck = False: Exit For
        Next
    End If
End Sub

Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    'Checked ma khong can dung vong lap, ke ca khi shift va chon:
    ListView1.ListItems(Item.Index).Checked = True
    '---------------------------------------------------
    'Thu tuc duoi day de phuc vu cho su kien ColumnClick:
    If Item.Checked Then
        Idx = IIf(Idx > Item.Index, Idx, Item.Index)
        IsCheck = False
    Else
        IsCheck = True
        If Idx = 0 Then Exit Sub
        For i = 1 To Idx
            If ListView1.ListItems(i).Checked Then IsCheck = False: Exit For
        Next
    End If
End Sub

Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
    Dim LstItm As ListItem, i As Long
    If IsCheck = False Then
        'Nham gioi han vong lap, nen nhan thong so Idx:
        If Idx > 0 Then
            For i = 1 To Idx
                ListView1.ListItems(i).Checked = IsCheck
                ListView1.ListItems(i).Selected = IsCheck
            Next
            Idx = 0
        Else
             GoTo Else_If
        End If
    Else
Else_If:
        'Khi chon tat ca, hoac bo chon tat ca thi dung thu tuc duoi:
        For Each LstItm In ListView1.ListItems
            LstItm.Checked = IsCheck
            LstItm.Selected = IsCheck
        Next
    End If
    IsCheck = IIf(IsCheck, False, True)
End Sub

Như vậy thì, nếu select tại đâu thì check tại đó, nếu muốn bỏ check thì dùng tại checkbox, bỏ toàn bộ check thì click tiêu đề.
 

File đính kèm

Upvote 0
Không đúng đâu, vì:

1. Với thủ tục sự kiện ItemClick. Ngay phiên bản 1 mình cũng comment vào đó nhưng không dùng
[GPECODE=vb]
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
ListView1.ListItems(Item.Index).Checked = True
End Sub
[/GPECODE]

Bạn chi có select và không có hủy select. Ví dụ nhấp chuột vào dòng 3, giữ Shift nhấp chuột vào dòng 8 (khi đó ta có các dòng select), b giờ tôi muốn lùi về dòng 5 (muốn bỏ chọn 3 dòng thừa) thì không được. Các phương thức MultiSelect phải hủy select mới đúng.

2. Dùng IsCheck để xác định cột đã check hay chưa
Nếu dùng biến IsCheck thì nó dùng cho tấ cả các cột? Tại sao không dùng phương thức mình đã đưa ra ngay bài đầu là:
[GPECODE=vb]
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
ColumnHeader.Tag = Not CBool(IIf(ColumnHeader.Tag = "", 0, ColumnHeader.Tag))
CheckItems CBool(ColumnHeader.Tag)
End Sub

Private Function CheckItems(ByVal bCheck As Boolean) As Long
Dim I As Long
For I = 1 To ListView1.ListItems.Count
CheckItems = CheckItems + 1
ListView1.ListItems(I).Checked = bCheck
Next I
End Function
[/GPECODE]

Thuộc tính ColumnHeader.Tag sẽ ghi nhận trạng thái check của mỗi cột khi cột thay đổi.

Góp ý thêm
+ Với trường hợp phải dùng
IsCheck = IIf(IsCheck, False, True)

nên thay bằng
IsCheck = Not IsCheck

+ If IsCheck = False Then

Nên thay bằng
If Not IsCheck Then

Vì biểu thức logic luôn trả về giá trị logic (True/False). Bản thân biến IsCheck As Boolean luôn trả về giá trị logic rồi thì không cần ghép nó vào biểu thức nếu nó chỉ có 1 mình.
 
Lần chỉnh sửa cuối:
Upvote 0
Không đúng đâu, vì:

1. Với thủ tục sự kiện ItemClick. Ngay phiên bản 1 mình cũng comment vào đó nhưng không dùng
[GPECODE=vb]
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
ListView1.ListItems(Item.Index).Checked = True
End Sub
[/GPECODE]

Bạn chi có select và không có hủy select. Ví dụ nhấp chuột vào dòng 3, giữ Shift nhấp chuột vào dòng 8 (khi đó ta có các dòng select), b giờ tôi muốn lùi về dòng 5 (muốn bỏ chọn 3 dòng thừa) thì không được. Các phương thức MultiSelect phải hủy select mới đúng.

2. Dùng IsCheck để xác định cột đã check hay chưa
Nếu dùng biến IsCheck thì nó dùng cho tấ cả các cột? Tại sao không dùng phương thức mình đã đưa ra ngay bài đầu là:
[GPECODE=vb]
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
ColumnHeader.Tag = Not CBool(IIf(ColumnHeader.Tag = "", 0, ColumnHeader.Tag))
CheckItems CBool(ColumnHeader.Tag)
End Sub

Private Function CheckItems(ByVal bCheck As Boolean) As Long
Dim I As Long
For I = 1 To ListView1.ListItems.Count
CheckItems = CheckItems + 1
ListView1.ListItems(I).Checked = bCheck
Next I
End Function
[/GPECODE]

Thuộc tính ColumnHeader.Tag sẽ ghi nhận trạng thái check của mỗi cột khi cột thay đổi.

Góp ý thêm
+ Với trường hợp phải dùng
IsCheck = IIf(IsCheck, False, True)

nên thay bằng
IsCheck = Not IsCheck

+ If IsCheck = False Then

Nên thay bằng
If Not IsCheck Then

Vì biểu thức logic luôn trả về giá trị logic (True/False). Bản thân biến IsCheck As Boolean luôn trả về giá trị logic rồi thì không cần ghép nó vào biểu thức nếu nó chỉ có 1 mình.

Em cố tình làm cho select nhận giá trị Check mà không UnCheck, bởi khi ta chọn nhiều vùng không liên tục, nếu không có select thì nó lại trả về UnCheck các vùng ta đã chọn, nên chọn kiểu đó không khả thi.

Còn nếu Anh Tuân muốn nó có luôn giá trị đó thì rất đơn giản:

Mã:
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    With ListView1.ListItems(Item.Index)
        If .Checked Then .Checked = False Else .Checked = True
    End With
End Sub
 
Upvote 0
Em cố tình làm cho select nhận giá trị Check mà không UnCheck, bởi khi ta chọn nhiều vùng không liên tục, nếu không có select thì nó lại trả về UnCheck các vùng ta đã chọn, nên chọn kiểu đó không khả thi.

Còn nếu Anh Tuân muốn nó có luôn giá trị đó thì rất đơn giản:

Mã:
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    With ListView1.ListItems(Item.Index)
        If .Checked Then .Checked = False Else .Checked = True
    End With
End Sub

Với đoạn code trên thì thay bằng
Mã:
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    With Item
        If .Checked Then .Checked = False Else .Checked = True
    End With
End Sub
Vì Item chính là đối tượng được chọn.

Nhưng Nghĩa đã chạy thử chưa? Mình không thấy chạy đúng.
 
Upvote 0
Với đoạn code trên thì thay bằng
Mã:
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    With Item
        If .Checked Then .Checked = False Else .Checked = True
    End With
End Sub
Vì Item chính là đối tượng được chọn.

Nhưng Nghĩa đã chạy thử chưa? Mình không thấy chạy đúng.

Thấy nó không bao 2 đầu khi shift, vậy phải như thế nào nữa nhỉ? Thêm thuộc tính KeyUp nữa được không ta? Với vòng lặp mà chạy vài chục ngàn dòng trong ListView thì có khi chậm quá, nên nghĩ ra hướng nào đó nhanh hơn thôi, chứ của Anh Tuân là chuẩn lắm rồi.
 
Upvote 0
2. Dùng IsCheck để xác định cột đã check hay chưa
Nếu dùng biến IsCheck thì nó dùng cho tấ cả các cột?

Em nghĩ thêm rằng, nếu muốn xác định cột nào thực hiện thì làm như sau:

Mã:
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
    If ColumnHeader = "ITEMS" Then
            '---------------------
    End If
End Sub

hoặc:

Mã:
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
    If ColumnHeader.Index = 1 Then
            '---------------------
    End If
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Mình thử thế này có được không? Hơi củ chuối một chút nhưng làm theo cách là bẫy sự kiện mouseup và keyup
 

File đính kèm

Upvote 0
Upvote 0
Set check sate with selected items in ListView

Đã có giải pháp rồi đây. Dù item có lên 10.000 hay nhiều hơn tốc độ cũng ok.

Toàn bộ mã nguồn chỉnh sửa thế này:
[GPECODE=vb]
Option Explicit
Private IdxOfFirst As Long, IdxOfSecond As Long
Private IsShiftKeyPressed As Boolean, IsCtrlKeyPressed As Boolean
Private MustClearAll As Boolean

Private Sub ListView1_ItemCheck(ByVal Item As MSComctlLib.ListItem)
'when mouse click on item to set check state
MustClearAll = True
End Sub

Private Sub UserForm_Initialize()
Dim I As Long
With ListView1
.MultiSelect = True 'Cho phep chon nhieu item va chay CheckItemsWithSelectState trong ListView1_ItemClick
.ColumnHeaders.Add , , "VALUE", .Width - 4
.CheckBoxes = True
For I = 1 To 10000
.ListItems.Add , , "Item" & I
Next
IdxOfFirst = .SelectedItem.Index
IdxOfSecond = IdxOfFirst
End With
End Sub

Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
ColumnHeader.Tag = Not CBool(IIf(ColumnHeader.Tag = "", 0, ColumnHeader.Tag))
CheckItems CBool(ColumnHeader.Tag)
MustClearAll = CBool(ColumnHeader.Tag)
End Sub

Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
If ListView1.MultiSelect Then
CheckItemsWithSelectState Item
End If
End Sub

Private Function CheckItems(ByVal bCheck As Boolean) As Long
Dim I As Long
For I = 1 To ListView1.ListItems.Count
CheckItems = CheckItems + 1
ListView1.ListItems(I).Checked = bCheck
Next I
End Function

Private Function CheckItemsWithSelectState(ByVal Item As ListItem) As Long
Dim I As Long

IsShiftKeyPressed = IsShiftKeyDown()
IsCtrlKeyPressed = IsControlKeyDown()

If IsCtrlKeyPressed Then
Item.Checked = Item.Selected
MustClearAll = True
Exit Function
End If

'If no shift, no ctrl press then check to clear check state for all items
If Not (IsShiftKeyPressed Or IsCtrlKeyPressed) Then
If MustClearAll Then
MustClearAll = False
CheckItems False
End If
End If

'Clear old seleted items
For I = IdxOfFirst To IdxOfSecond
ListView1.ListItems(I).Checked = False
Next I
' find IdxOfFirst from current index
For I = Item.Index To 1 Step -1
If Not ListView1.ListItems(I).Selected Then
Exit For
End If
IdxOfFirst = I
Next I
' find IdxOfSecond from current index
For I = Item.Index To ListView1.ListItems.Count
If Not ListView1.ListItems(I).Selected Then
Exit For
End If
IdxOfSecond = I
Next I

'Set check state for new selected items
For I = IdxOfFirst To IdxOfSecond
ListView1.ListItems(I).Checked = ListView1.ListItems(I).Selected
Next I

End Function

[/GPECODE]

thêm một modKeyState. Tất cả nằm trong file đính kèm.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Em thử chọn nhấn Shift với gần 10.000 đối tượng thì máy treo luôn, nhưng nhấn cột tiêu đề chọn hết chạy tốt, nhanh

Hi, nếu chọn hết thì dùng nhấp chuột vào cột, còn dùng Shift bị chậm mình sẽ tìm thêm cách khắc phục.
 
Upvote 0
Hi, nếu chọn hết thì dùng nhấp chuột vào cột, còn dùng Shift bị chậm mình sẽ tìm thêm cách khắc phục.

Có một điều là khi chọn checkbox thì nó cho ta nhiều chọn lựa, thế nhưng nếu select gặp check thì uncheck, gặp uncheck thì check thì không ổn, nó mang tính của option chứ không còn của checkbox nữa, vì thế theo em nghĩ chỉ cần dùng select vào việc check là được rồi, còn uncheck xin để cho anh checkbox ảnh lo. Bởi nếu ta chọn nhiều vùng, rồi lại chọn từng mục thì những mục đã check sẳn bị mất check.
 
Upvote 0
Có một điều là khi chọn checkbox thì nó cho ta nhiều chọn lựa, thế nhưng nếu select gặp check thì uncheck, gặp uncheck thì check thì không ổn, nó mang tính của option chứ không còn của checkbox nữa, vì thế theo em nghĩ chỉ cần dùng select vào việc check là được rồi, còn uncheck xin để cho anh checkbox ảnh lo. Bởi nếu ta chọn nhiều vùng, rồi lại chọn từng mục thì những mục đã check sẳn bị mất check.

Mình nghĩ cứ theo logic của ListView trong MyComputer là chuẩn. Select vừa dùng để chọn cũng vừa để không chọn. Khi có check button thì nó dùng để tuỳ ý thêm hay bỏ bớt các item.
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom