ListView: Làm sao có thể check nhiều trong 1 lần thao tác. (1 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,725
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
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.

Theo em hiểu thì ListView trong MyComputer mới có dạng Select thôi, với listview mình đang hướng tới nó có checkbox cho ta lựa chọn. Nếu đã không dùng checkbox thì mình cần chi phải nhọc công viết code, vì thuộc tính MultiSelect đã có sẳn rồi, viết thêm chi nữa phải không?
 
Upvote 0
Phiên bản mới đây. Tốc độ nhanh hơn bản cũ. Khắc phục tốc độ bị chậm khi dùng Shift để chọn nhiều item.

[GPECODE=vb]
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Set check sate with selected items in ListView
' By Nguyen Duy Tuan (duytuan@bluesofs.net) www.giaiphapexcel.com
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private IdxOfFirst As Long, IdxOfSecond As Long
Private IsShiftKeyPressed As Boolean, IsCtrlKeyPressed As Boolean
Private MustClearAll As Boolean, IsClickCheck As Boolean
Dim CurrentItem As MSComctlLib.ListItem

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), Nothing, True
MustClearAll = CBool(ColumnHeader.Tag)
End Sub

Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
Set CurrentItem = Item
End Sub

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

Private Sub ListView1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
If IsClickCheck Then
IsClickCheck = False
Exit Sub
End If
If Not CurrentItem Is Nothing Then
If ListView1.MultiSelect Then
CheckItemsWithSelectState CurrentItem
End If
End If
End Sub

Private Function CheckItems(ByVal bCheck As Boolean, ByVal Item As ListItem, ByVal bSelectIfChecked As Boolean) As Long
Dim I As Long, IdxCurrent As Long

If Not Item Is Nothing Then
IdxCurrent = Item.Index
End If

Dim ItemVisible As MSComctlLib.ListItem
Set ItemVisible = ListView1.GetFirstVisible

For I = ItemVisible.Index To ListView1.ListItems.Count
CheckItems = CheckItems + 1
If IdxCurrent <> I Then 'don't set state for current index
If ListView1.ListItems(I).Checked <> bCheck Then
ListView1.ListItems(I).Checked = bCheck
End If
If bSelectIfChecked Then
ListView1.ListItems(I).Selected = bCheck
End If
End If
DoEvents
Next I

For I = ItemVisible.Index To 1 Step -1
CheckItems = CheckItems + 1
If IdxCurrent <> I Then 'don't set state for current index
If ListView1.ListItems(I).Checked <> bCheck Then
ListView1.ListItems(I).Checked = bCheck
End If
If bSelectIfChecked Then
ListView1.ListItems(I).Selected = bCheck
End If
End If
DoEvents
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
Item.Checked = Item.Selected
CheckItems False, Item, False
End If
End If

'Clear old seleted items
For I = IdxOfFirst To IdxOfSecond
If I <> Item.Index Then
ListView1.ListItems(I).Checked = False
End If
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
Dim ItemVisible As MSComctlLib.ListItem
Set ItemVisible = ListView1.GetFirstVisible
For I = ItemVisible.Index To IdxOfSecond
ListView1.ListItems(I).Checked = ListView1.ListItems(I).Selected
DoEvents
Next I
For I = ItemVisible.Index To IdxOfFirst Step -1
ListView1.ListItems(I).Checked = ListView1.ListItems(I).Selected
DoEvents
Next I
End Function
[/GPECODE]
 

File đính kèm

Upvote 0
Phiên bản mới đây. Tốc độ nhanh hơn bản cũ. Khắc phục tốc độ bị chậm khi dùng Shift để chọn nhiều item.

Phải công nhận cái thằng "CheckItems" của Anh nó chạy nhanh "ve sầu" thiệt! Hình như nó bỏ qua update screen và chỉ hiển thị cái đang có trên màn hình thôi nên tốc độ được cải thiện đáng kể!
 
Upvote 0
Phải công nhận cái thằng "CheckItems" của Anh nó chạy nhanh "ve sầu" thiệt! Hình như nó bỏ qua update screen và chỉ hiển thị cái đang có trên màn hình thôi nên tốc độ được cải thiện đáng kể!

Hi. Hai hàm chủ đạo để thiết lập trạng thái check là "CheckItems" và "CheckItemsWithSelectState". Tốc độ nhanh bởi các phương pháp và tiểu xảo:

+ Ưu tiên vẽ cái đang cần nhìn thấy trước
Ví dụ có các item 1,2,3,4,5,6,7. Màn hình ListView chỉ nhìn thấy 3,4,5 (còn 1,2,6,7 bị khuất) vậy phải tìm ra cái item 3 (bằng lệnh Set ItemVisible = ListView1.GetFirstVisible) sau đó vẽ từ 3 cho đến hết, tiếp sau vẽ từ 3 chạy ngược về 1. Trong thực tế, người dùng đang xem phần cuối của danh sách có 100.000 dòng thì sẽ thấy nó không khác mấy so với 10 dòng. Khi người dùng cuộn danh sách thì bao giờ nó cũng chạy tuần tự từ cái hiện tại xuống dưới hoặc lên trên.

+ Trong khi vẽ phải bỏ qua cái đã check (cái item hiện tại đang chọn).
Trong hàm CheckItemsWithSelectState có đoạn
[GPECODE=vb] If Not (IsShiftKeyPressed Or IsCtrlKeyPressed) Then
If MustClearAll Then
MustClearAll = False
Item.Checked = Item.Selected 'Thiết lập item hiện tại
CheckItems False, Item, False 'Xoá check của tất cả trừ cái hiện tại
End If
End If[/GPECODE]

Có nghĩa là khi không có phím Shift, Ctrl được nhấn, tức là chỉ chọn 1 item, thì cần xoá trạng thái check của toàn bộ ListView. Nhưng hãy thiết lập cái hiện tại trước để người ta nhìn thấy đã sau đó mới làm cái việc xoá tất cả trạng thái check trừ cái hiện tại. Thứ tự hai dòng lệnh này hoàn oàn có ý nghĩa, vì xoá sẽ mất time nhiều hơn nên để sau.

+ Trong khi vẽ nên kiểm tra trạng thái, nếu khác trạng thái mới thiết lập
[GPECODE=vb] If ListView1.ListItems(I).Checked <> bCheck Then
ListView1.ListItems(I).Checked = bCheck
End If[/GPECODE]
Phương pháp này nên làm mặc dù trong ListView đã kiểm tra trước khi thiết lập mới.

+ Trong quá trình thiết lập trong vòng lặp, nên dùng lệnh cập nhật sự kiện của hệ thống
[GPECODE=vb]DoEvents[/GPECODE]

Với lệnh trên ta không thấy cảm giác đơ chuột, bàn phím và màn hình được update.

Nói chung, để chạy nhanh vừa phải dùng phương pháp vừa tiểu xảo __--__.
 
Lần chỉnh sửa cuối:
Upvote 0
Thấy chủ đề này hay hay, mình xin phép mạo muội nghiên cứu. Thế mà thoáng cái đã thấy Duy Tuân ra một phiên bản và và ý tường của mình thành lỗi thời rồi...hehe. Tuy nhiên, mình cũng muốn chia sẻ một tiếp cận khá tương tự sử dụng bản chuối đầu tiên của mình.
Nguyên tắc giống hệt của Tuân nhưng có hơi khác một chút đó là mình dựa vào ứng xử của người dùng để quyết định xem có lặp vài chục ngàn lần hay chỉ là đối với những gì hiển thị trên màn hình. Ngoài ra có ăn trộm thêm ý tưởng của Tuân là đánh lừa người dùng....
Xin góp vui chút ít.

PS. à bản của Tuân có một chút chưa thấy đưa ra là: nếu người dùng giữ SHIFT và nhấn HOME hoặc END hoặc PAGE UP/ DOWN thì không có hiệu ứng check.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Thấy chủ đề này hay hay, mình xin phép mạo muội nghiên cứu. Thế mà thoáng cái đã thấy Duy Tuân ra một phiên bản và và ý tường của mình thành lỗi thời rồi...hehe. Tuy nhiên, mình cũng muốn chia sẻ một tiếp cận khá tương tự sử dụng bản chuối đầu tiên của mình.
Nguyên tắc giống hệt của Tuân nhưng có hơi khác một chút đó là mình dựa vào ứng xử của người dùng để quyết định xem có lặp vài chục ngàn lần hay chỉ là đối với những gì hiển thị trên màn hình. Ngoài ra có ăn trộm thêm ý tưởng của Tuân là đánh lừa người dùng....
Xin góp vui chút ít.

PS. à bản của Tuân có một chút chưa thấy đưa ra là: nếu người dùng giữ SHIFT và nhấn HOME hoặc END hoặc PAGE UP/ DOWN thì không có hiệu ứng check.

Em vừa test bản của anh thì thấy tư duy cũng giống kiểu của em, nhưng anh dùng mảng để ghi lại danh sách Index để cập nhật, em thì không. Khi chạy cái của anh em thấy có vấn đề là:

+ Bấm chuột vào nút checkbox không được
+ Hàm GetVisibleItem bị lỗi, nó chỉ nhận danh sách những item đang nhìn thấy. Khi bấm chuột vào một mục, cuộn xuống item bị khuất (kiểu như chọn dòng của trang tiếp theo) thì nó bỏ mất các mục bị ẩn khuất phía trên.
Ví dụ có các item:1,2,3,4,5,6,7
ListView đang thấy 1,2,3 em chọn item 1, giưcx phím Shift sau đó cuộn để chọn 6 thì item 2,3 không được check.
 
Upvote 0
Hihi, thì mình chỉ muốn đóng góp thêm một cái lá già để cho thêm phần hưng phấn thôi mà. Để hoàn thiện tiếp thì chắc phải phân tích thêm tí nữa để hết mọi tình huống. Bản của Tuân đã quá đủ để giải quyết các vấn đề đặt ra ban đầu của bài tập rồi mà.
Xin cảm ơn Tuân đã dành thời gian ngó qua. Tớ học và ăn trộm được qua chủ đề này cũng nhiều đấy.
Đa tạ
 
Upvote 0

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

Back
Top Bottom