Thêm, Sửa, Xóa trên ListView

Liên hệ QC

msc0506

Thành viên chính thức
Tham gia
14/4/08
Bài viết
56
Được thích
12
Mình đang cần code thêm sửa xóa trực tiếp trên ListView . Cao thủ nào đã vọc rồi hoặc có ví dụ về vấn đề này giúp mình với nhé, Minh đang cần

Cảm ơn trước
 
Xoá thằng nào trên listview thì dùng lệnh sau ví dụ dòng 2

Me.ListView1.ListItems.Remove (2)

Xoá trên sheet thì dùng find hay index để tìm dòng cần xoá như bình thường
 
Upvote 0
Xoá thằng nào trên listview thì dùng lệnh sau ví dụ dòng 2

Me.ListView1.ListItems.Remove (2)

Xoá trên sheet thì dùng find hay index để tìm dòng cần xoá như bình thường

Dạ, em biết là vậy với điều kiện là xóa từng "thằng" một, nhưng vấn đề là khi mình check nhiều "thằng" và xóa hàng loạt những "thằng" đã check thì làm sao đó mà! Hỏng lẻ không thể làm được hả Anh? Chắc phải có chứ, vì nó đã tạo ra cái checkbox trên listview mà ta?!
 
Lần chỉnh sửa cuối:
Upvote 0
Dạ, em biết là vậy với điều kiện là xóa từng "thằng" một, nhưng vấn đề là khi mình check nhiều "thằng" và xóa hàng loạt những "thằng" đã check thì làm sao đó mà! Hỏng lẻ không thể làm được hả Anh? Chắc phải có chứ, vì nó đã tạo ra cái checkbox trên listview mà ta?!
tiếp tục For next!

Ku nào check = True thì xoá.
 
Upvote 0
Dạ, em biết là vậy với điều kiện là xóa từng "thằng" một, nhưng vấn đề là khi mình check nhiều "thằng" và xóa hàng loạt những "thằng" đã check thì làm sao đó mà! Hỏng lẻ không thể làm được hả Anh? Chắc phải có chứ, vì nó đã tạo ra cái checkbox trên listview mà ta?!
Thì như sư phụ Mỹ đã nói: For... Next, xét em nào Checked = True thì Remove
Có điều phải lưu ý: For Next ngược từ dưới lên:
PHP:
  Dim i As Long
  With ListView1
    For i = .ListItems.Count To 1 Step -1
      If .ListItems(i).Checked Then .ListItems.Remove i
    Next
  End With
 
Upvote 0
Cám ơn các Sư Phụ đã hướng dẫn em, tuy nhiên em còn một thắc mắc nữa là không biết trên máy khác thì sao, song, máy của em thì khi remove Item thì nó không tự dồn hàng lên như ta delete row trong sheet (mặc dù em đã chạy Listview1.Refresh). Vậy làm sao cho nó dồn hàng lên sau khi xóa ạ?
 
Upvote 0
Bạn thay đoạn code sau rồi test nghiên cứu lý do nha

Mã:
Private Sub UserForm_Initialize()
Dim It As ListItem
Dim i
[U]Me.ListView1.View = lvwReport
Me.ListView1.ColumnHeaders.Add 1, , "Danh Sach", 110[/U]
For i = 1 To 50
Me.ListView1.ListItems.Add , , Sheet1.Cells(i, 1)
Next
For i = 1 To 12
Me.ListView1.ListItems.Add , , ""
Next
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn thay đoạn code sau rồi test nghiên cứu lý do nha

Mã:
Private Sub UserForm_Initialize()
Dim It As ListItem
Dim i
[U]Me.ListView1.View = lvwReport[/U]
[U]Me.ListView1.ColumnHeaders.Add 1, , "Danh Sach", 110[/U]
For i = 1 To 50
Me.ListView1.ListItems.Add , , Sheet1.Cells(i, 1)
Next
For i = 1 To 12
Me.ListView1.ListItems.Add , , ""
Next
End Sub

Theo code của Anh Sealand thì em nghiệm ra rằng, thứ nhất là phải chọn thuộc tính View là lvwReport, thứ hai là phải thêm header cho cột.

Vấn đề xóa nhiều check thì em làm như sau:

PHP:
Sub DeleteListItemsChecked()
  Dim i As Long, j As Long, Rng As Range
  With ListView1
    For i = 1 To .ListItems.Count
      If .ListItems(i).Checked = True Then
        With Sheet1.[A1:A50]
          Set Rng = .Find(ListView1.ListItems(i), LookIn:=xlValues, LookAt:=xlWhole)
          ''If Not Rng Is Nothing Then Sheet1.Rows(Rng.Row).Delete
          If Not Rng Is Nothing Then Sheet1.Cells(Rng.Row, 1).Delete 2
        End With
      End If
    Next
    For j = .ListItems.Count To 1 Step -1
      If .ListItems(j).Checked Then .ListItems.Remove j
    Next
  End With
End Sub

Lưu ý, với ListItems phải là không có dấu tiếng Việt (unicode) và ListItems không bị trùng; còn nếu nguồn trong sheet là font Unicode và đã convert từ Unicode sang VNI trong Listview thì phải chuyển sang VNI sang Unicode trong Find thì mới xóa chính xác.

Cám ơn Anh NDU và Anh Seland đã tận tình giúp đỡ em.
 
Lần chỉnh sửa cuối:
Upvote 0
Theo code của Anh Sealand thì em nghiệm ra rằng, thứ nhất là phải chọn thuộc tính View là lvwReport, thứ hai là phải thêm header cho cột.

Vấn đề xóa nhiều check thì em làm như sau:

PHP:
Sub DeleteListItemsChecked()
  Dim i As Long, j As Long, Rng As Range
  With ListView1
    For i = 1 To .ListItems.Count
      If .ListItems(i).Checked = True Then
        With Sheet1.[A1:A50]
          Set Rng = .Find(ListView1.ListItems(i), LookIn:=xlValues, LookAt:=xlWhole)
          ''If Not Rng Is Nothing Then Sheet1.Rows(Rng.Row).Delete
          If Not Rng Is Nothing Then Sheet1.Cells(Rng.Row, 1).Delete 2
        End With
      End If
    Next
    For j = .ListItems.Count To 1 Step -1
      If .ListItems(j).Checked Then .ListItems.Remove j
    Next
  End With
End Sub

Lưu ý, với ListItems phải là không có dấu tiếng Việt (unicode) và ListItems không bị trùng; còn nếu nguồn trong sheet là font Unicode và đã convert từ Unicode sang VNI trong Listview thì phải chuyển sang VNI sang Unicode trong Find thì mới xóa chính xác.

Cám ơn Anh NDU và Anh Seland đã tận tình giúp đỡ em.
Xóa Item trong Listview và xóa dữ liệu trên sheet sao không làm 1 lần luôn mà phải chia ra 2 vòng lập For thế nhỉ?
 
Upvote 0
Xóa Item trong Listview và xóa dữ liệu trên sheet sao không làm 1 lần luôn mà phải chia ra 2 vòng lập For thế nhỉ?
Đúng rồi, tại em lúc đầu thử 2 cái riêng biệt rồi ghép lại, thêm nữa lúc đầu ListItems có dấu tiếng Việt, em chạy hoài nó không xóa, sau khi thử nhiều cách rồi mới biết nguyên nhân là vậy, rồi không kiểm tra lại vòng lặp.

Cám ơn Anh NDU rất nhiều!!!!

PHP:
Private Sub CommandButton2_Click()
  Dim i As Long, Rng As Range
  With ListView1
    For i = .ListItems.Count To 1 Step -1
      If .ListItems(i).Checked Then
        With Sheet1.[A1:A50]
          Set Rng = .Find(ListView1.ListItems(i), LookIn:=xlValues, LookAt:=xlWhole)
          If Not Rng Is Nothing Then Sheet1.Range("A" & Rng.Row, "B" & Rng.Row).Delete 2
        End With
        .ListItems.Remove i
      End If
    Next
  End With
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
1/Listview mà bạn đang thiết lập ở dạng 1-lvwsmallIcon nên nó có thể di chuyển các Item bằng chuột và không tự dồn dịch được
2/Mình cũng đã sử dụng Listview để tìm xoá thì chắc ăn nhất là thêm cột số dòng và cho độ rộng dòng này bằng không (Nó không hiện ra). Khi xoá thằng nào thì cứ xem số tại cột đó bằng bao nhiêu rồi xoá dòng đó là chắc như đinh.
3/Code của bạn xoá sinh lỗi. Như Ndu đã cảnh báo, bạn phải soát ngược vòng For tránh biến đếm vượt số Item do bị xoá bớt
 
Lần chỉnh sửa cuối:
Upvote 0
2/Mình cũng đã sử dụng Listview để tìm xoá thì chắc ăn nhất là thêm cột số dòng và cho độ rộng dòng này bằng không (Nó không hiện ra). Khi xoá thằng nào thì cứ xem số tại cột đó bằng bao nhiêu rồi xoá dòng đó là chắc như đinh.
Em nghĩ đâu cần thiết phải thêm cột hả anh!
Ví dụ: SrcRng là vùng dữ liệu mà ta Add vào ListView, vậy thì trên ListView, khi ta check tại mục số 5, cũng hoàn toàn tương đương với SrcRng(5,1) trên sheet ---> Cứ thế mà xóa thôi
(Đang nói SrcRng là dữ liệu 1 cột nhiều dòng)
 
Upvote 0
Em nghĩ đâu cần thiết phải thêm cột hả anh!
Ví dụ: SrcRng là vùng dữ liệu mà ta Add vào ListView, vậy thì trên ListView, khi ta check tại mục số 5, cũng hoàn toàn tương đương với SrcRng(5,1) trên sheet ---> Cứ thế mà xóa thôi
(Đang nói SrcRng là dữ liệu 1 cột nhiều dòng)

Đúng như Anh nói, tốt nhất ta định vị hàng trên sheet và ListItems(i).Index rồi xóa là tốt nhất, chắc chắn là không bao giờ sai, với điều kiện xóa từ dưới lên trên (chứ từ trên xuống dưới cũng die luôn).

Em làm như sau:

PHP:
Option Explicit
Dim lsvItem As ListItem, i As Long, j As Long

Private Sub UserForm_Initialize()
  With ListView1
    .ColumnHeaders.Clear: .ListItems.Clear
    For i = 1 To 2
      .ColumnHeaders.Add , , Sheet1.Cells(1, i)
      .ColumnHeaders(i).Width = 130
    Next
    For j = 1 To Sheet1.[A5000].End(xlUp).Row - 1
      Set lsvItem = .ListItems.Add(, , Sheet1.Cells(j + 1, "A"))
      lsvItem.SubItems(1) = Sheet1.Cells(j + 1, 2)
    Next
  End With
End Sub

PHP:
Private Sub CommandButton1_Click()
  If CommandButton1.Caption = "Check ALL" Then
    For Each lsvItem In Me.ListView1.ListItems
      lsvItem.Checked = True
    Next
    CommandButton1.Caption = "UnCheck ALL"
  Else
    For Each lsvItem In Me.ListView1.ListItems
      lsvItem.Checked = False
    Next
    CommandButton1.Caption = "Check ALL"
  End If
End Sub

PHP:
Private Sub CommandButton2_Click()
  With ListView1
    For i = .ListItems.Count To 1 Step -1
      If .ListItems(i).Checked Then
        j = .ListItems(i).Index + 1
        Sheet1.Range("A" & j, "B" & j).Delete 2
        .ListItems.Remove i
      End If
    Next
  End With
End Sub

Như vậy sẽ đơn giản hơn là dùng hàm FIND.
 

File đính kèm

  • HOC_LISTVIEW.xls
    49.5 KB · Đọc: 131
Lần chỉnh sửa cuối:
Upvote 0
Em nghĩ đâu cần thiết phải thêm cột hả anh!
Ví dụ: SrcRng là vùng dữ liệu mà ta Add vào ListView, vậy thì trên ListView, khi ta check tại mục số 5, cũng hoàn toàn tương đương với SrcRng(5,1) trên sheet ---> Cứ thế mà xóa thôi
(Đang nói SrcRng là dữ liệu 1 cột nhiều dòng)

listview nó có phương thức sort, vậy mà dựa vào index thì dễ oan gia lắm.
 
Upvote 0
Đoạn này
PHP:
Private Sub CommandButton1_Click()
  Dim lsvItem As ListItem
  If CommandButton1.Caption = "Check ALL" Then
    For Each lsvItem In Me.ListView1.ListItems
      lsvItem.Checked = True
    Next
    CommandButton1.Caption = "UnCheck ALL"
  Else
    For Each lsvItem In Me.ListView1.ListItems
      lsvItem.Checked = False
    Next
    CommandButton1.Caption = "Check ALL"
  End If
End Sub
Ai lại làm thế!
Tôi sẽ làm vầy:
PHP:
Private Sub CommandButton1_Click()
  Dim lsvItem As ListItem
  With CommandButton1
    For Each lsvItem In Me.ListView1.ListItems
      lsvItem.Checked = .Caption = "Check ALL"
    Next
    .Caption = IIf(.Caption = "Check ALL", "UnCheck ALL", "Check ALL")
  End With
End Sub
Có ngắn gọn và đơn giản hơn không?
 
Upvote 0
listview nó có phương thức sort, vậy mà dựa vào index thì dễ oan gia lắm.

Đã kiểm tra, nếu mà SORT thì oan gia thiệt, thử với nó là biết liền! Hic, Hic

PHP:
Private Sub ListViewSort(mLView As ListView, ByVal ColumnHeader As MSComctlLib.ColumnHeader)
  With mLView
    .Sorted = True
    .SortKey = ColumnHeader.SubItemIndex
    If .SortOrder = lvwDescending Then
      .SortOrder = lvwAscending
    Else
      .SortOrder = lvwDescending
    End If
    .Sorted = False
  End With
End Sub

PHP:
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
  Call ListViewSort(ListView1, ColumnHeader)
End Sub

Như vậy, muốn dùm hàm FIND hay INDEX để xóa trong sheet, thì người thiết kế phải nắm chắc cơ sở dữ liệu của mình như thế nào để không bị mất dữ liệu "oan". VD, nếu dùng INDEX thì không cho thuộc tính SORT, nếu dùng FIND thì dữ liệu phải không trùng... Có như vậy mới chắc chắn rằng mình xóa "đúng người đúng tội".
 
Lần chỉnh sửa cuối:
Upvote 0
Đã kiểm tra, nếu mà SORT thì oan gia thiệt, thử với nó là biết liền! Hic, Hic

PHP:
Private Sub ListViewSort(mLView As ListView, ByVal ColumnHeader As MSComctlLib.ColumnHeader)
  With mLView
    .Sorted = True
    .SortKey = ColumnHeader.SubItemIndex
    If .SortOrder = lvwDescending Then
      .SortOrder = lvwAscending
    Else
      .SortOrder = lvwDescending
    End If
    .Sorted = False
  End With
End Sub
PHP:
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
  Call ListViewSort(ListView1, ColumnHeader)
End Sub
Như vậy, muốn dùm hàm FIND hay INDEX để xóa trong sheet, thì người thiết kế phải nắm chắc cơ sở dữ liệu của mình như thế nào để không bị mất dữ liệu "oan". VD, nếu dùng INDEX thì không cho thuộc tính SORT, nếu dùng FIND thì dữ liệu phải không trùng... Có như vậy mới chắc chắn rằng mình xóa "đúng người đúng tội".
Thật ra cũng chẳng hề gì nếu bạn... khéo
Cứ cho rằng chúng ta sẽ sort trên Listview đi, vậy thì ngay lúc AddItem cho Listview, ta cho list ấy vào 1 Dictionary Object với Dic.Key là các phần tử của Listview còn Dic.Item là STT ---> Mai này tìm kiếm thì cứ tra vào Dictionary mà tìm ra STT tương ứng
Có vấn đề gì không?
Bảo đảm với bạn rằng Find Method không sao bằng tốc độ so với dùng Array đâu
Ôi... vô vàn cách để nghiên cứu, nhưng cách dùng cột phụ như anh sealand thì em cho là... không được "đẹp" lắm
Ẹc... Ẹc...
 
Lần chỉnh sửa cuối:
Upvote 0
Thật ra cũng chẳng hề gì nếu bạn... khéo
Cứ cho rằng chúng ta sẽ sort trên Listview đi, vậy thì ngay lúc AddItem cho Listview, ta cho list ấy vào 1 Dictionary Object với Dic.Key là các phần tử của Listview còn Dic.Item là STT ---> Mai này tìm kiếm thì cứ tra vào Dictionary mà tìm ra STT tương ứng
Có vấn đề gì không?
Bảo đảm với bạn rằng Find Method không sao bằng tốc độ so với dùng Array đâu
Ôi... vô vàn cách để nghiên cứu, nhưng cách dùng cột phụ như anh sealand thì em cho là... không được "đẹp" lắm
Ẹc... Ẹc...

Cũng không sao Anh NDU ơi, với trình độ của em thì làm gì biết cách Dictionary Oject chứ, nhưng nếu làm cột phụ sau đó add số dòng lên đó, rồi cho Listview1.ColumnHeaders(cột phụ).Width = 0 thì nhìn vào ai biết gì mà xấu phải không?

Kiểm tra lại, nếu dùng cột phụ rồi add số hàng lên, sau khi sort cũng bị dính chưởng như thường! Ẹc ... Ẹc ...

PHP:
Private Sub UserForm_Initialize()
  With ListView1
    .ColumnHeaders.Clear: .ListItems.Clear
    For i = 1 To 2
      .ColumnHeaders.Add , , Sheet1.Cells(1, i)
      .ColumnHeaders(i).Width = 130
    Next
      .ColumnHeaders.Add , , "LINE"
      .ColumnHeaders(3).Width = 0
    For j = 1 To Sheet1.[A5000].End(xlUp).Row - 1
      Set lsvItem = .ListItems.Add(, , Sheet1.Cells(j + 1, "A"))
      For k = 1 To 2
        Select Case k
          Case 2: lsvItem.SubItems(k) = Format(Cells(j + 1, k + 1).Row, "00000") '<-- dung de sort moi dinh dang
          Case Else: lsvItem.SubItems(k) = Sheet1.Cells(j + 1, k + 1)
        End Select
    Next k, j
  End With
End Sub

PHP:
Private Sub CommandButton2_Click()
  With ListView1
    For i = .ListItems.Count To 1 Step -1
      If .ListItems(i).Checked Then
        j = .ListItems(i).ListSubItems(2)
        Sheet1.Range("A" & j, "B" & j).Delete 2
        .ListItems.Remove i
      End If
    Next
  End With
End Sub

Cách của Anh NDU về việc dùng DIC thì em không biết, nhưng chắc phải tùy thuộc vào cách mà mình quyết định trên Listview theo cơ sở dữ liệu của mình mà thực hiện thôi.
 
Lần chỉnh sửa cuối:
Upvote 0
Bảo đảm với bạn rằng Find Method không sao bằng tốc độ so với dùng Array đâu
Ôi... vô vàn cách để nghiên cứu, nhưng cách dùng cột phụ như anh sealand thì em cho là... không được "đẹp" lắm
Ẹc... Ẹc...

Mình lại nghĩ khác, khi sử lý các vấn đề phức tạp khác nó sẽ nảy sinh vấn đề xung đột. Luôn có 1 cái dictionnary tồn tại trong suốt quá trình tồn tại form. Động tác tra chưa chắc nhanh gọn hơn lấy subitem.
Mình lưu ý là để ở sublistem chứ không ở listitem.
 
Upvote 0
Mình lại nghĩ khác, khi sử lý các vấn đề phức tạp khác nó sẽ nảy sinh vấn đề xung đột. Luôn có 1 cái dictionnary tồn tại trong suốt quá trình tồn tại form. Động tác tra chưa chắc nhanh gọn hơn lấy subitem.
Mình lưu ý là để ở sublistem chứ không ở listitem.
Tra Dictionary và lấy Index là y chang nhau mà anh..
Và dù làm cách nào đi nữa thì sau khi xóa dữ liệu trên sheet, vẫn phải cập nhật lại STT thôi (cách của anh thì phải đánh lại STT ở SubItem, cách của em là nạp lại STT cho Dic.Item)
Vậy thôi! Ẹc... Ẹc...
 
Upvote 0
Em đã tìm ra giải pháp. Thay vì Delete Row, ta chỉ Clear tạm thời trong suốt quá trình hoạt động của Form, sau đó khi Form Unload ta xóa dòng trống là ổn cả, như vậy là tuyệt vời!

PHP:
Private Sub UserForm_Terminate()
  Call XoaDongTrong
End Sub
 
'----------------------------------------------
 
Private Sub XoaDongTrong()
  On Error Resume Next
  With Sheet1.UsedRange
    .SpecialCells(2).EntireRow.Hidden = True
    .SpecialCells(12).EntireRow.Delete
    .EntireRow.Hidden = False
  End With
End Sub

PHP:
Private Sub CommandButton2_Click()
  With ListView1
    For i = .ListItems.Count To 1 Step -1
      If .ListItems(i).Checked Then
        j = .ListItems(i).ListSubItems(2)
        Sheet1.Range("A" & j, "B" & j).Clear '<--- thay cho Delete
        .ListItems.Remove i
      End If
    Next
  End With
End Sub
 

File đính kèm

  • HOC_2_LISTVIEW.xls
    49 KB · Đọc: 101
Upvote 0
Web KT
Back
Top Bottom