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

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

Back
Top Bottom