Demo Form tìm kiếm nhiều cột trong Listbox

Liên hệ QC

ongke0711

Thành viên gắn bó
Tham gia
7/9/06
Bài viết
1,954
Được thích
2,534
Giới tính
Nam
Form tìm kiếm listbox thì diễn đàn cũng có nhiều bài rồi nhưng nó nằm chung trong các bài hỏi của thành viên nên tìm cũng mệt :). Vừa làm vừa học nên tôi tạo cái post này cho dễ tìm kiếm và nhờ các anh em giúp cải thiện cái form này cho chạy trơn tru và thuận tiện nhé.
- Form dùng tìm kiếm theo nhiều cột trong listbox. Chỉ cần gõ ký tự chuỗi bất kỳ, nó sẽ tìm trong tất cả các cột để lấy ra dòng có chứa chuỗi.
- Viết dạng hàm để có thể gọi sử dụng lại ở nhiều form.

Điểm chưa làm được:
- Chưa định dạng được dữ liệu hiển thị từng cột trong listbox (dùng mảng gán row source cho listbox). Cụ thể trong demo là cột [Đơn giá]: không có định dạng số (dấu cách phần ngàn .000).
- Chưa tuỳ chọn Sort dữ liệu tự động (theo cột đã chọn) trong listbox sau khi tìm kiếm (có tham khảo mấy hàm bubble sort trên mạng nhưng tích hợp vô).

Bạn nào chỉ cần tìm kiếm dữ liệu thì tôi nghĩ form này đáp ứng yêu cầu. :)
Các bạn hỗ trợ bổ sung thiết kế giùm nhé. File đính kèm bên dưới.
Cảm ơn.


Code cho Userform:

Mã:
Option Explicit

Dim oRngLstBx1 As Range

Private Sub txtChuoiTK_Change()
    Dim strTextSearch As String

    strTextSearch = Me.txtChuoiTK.Value
    Call faytLstBxMultiCol(oRngLstBx1, strTextSearch, "lstDanhSachVPP", Me)

End Sub

Private Sub UserForm_Initialize()
    ganSourceListbox
    Me.txtChuoiTK.SetFocus
End Sub


Sub ganSourceListbox()

    Dim sArr() As Variant

    Set oRngLstBx1 = Range(Range("A2"), Range("A2").End(xlDown).End(xlToRight))     'Khai báo cho biesn toàn cuc de su dung cho hàm TK
    ReDim sArr(1 To oRngLstBx1.Rows.Count, 1 To oRngLstBx1.Columns.Count)

    sArr = oRngLstBx1.Value
    Me.lstDanhSachVPP.List = sArr

End Sub


- Hàm faytLstBxMultiCol():

Mã:
Option Explicit

Function faytLstBxMultiCol(oLstBxRng As Range, strSearchTxt As String, strLstBxName As String, frm As UserForm) As Boolean
'----------------------------------------------------------
'# Hàm tim kiem,loc danh sach listbox theo chuoi tìm kiem.
'# oLstBxRng:       là Range làm Row Source cho Listbox.
'# strSearchTxt:    Chuoi can tim (tu textbox).
'# strLstBxName:    Ten cua listbox control tren userform.
'----------------------------------------------------------

On Error GoTo EH

    Dim sArr() As Variant
    Dim blnFound As Boolean 'bien neu tim thay chuoi can tìm
    Dim i As Long, j As Long, rCount As Long
    Dim oLstBx As Object

    faytLstBxMultiCol = False
    Set oLstBx = frm.Controls(strLstBxName)

    ReDim sArr(1 To oLstBxRng.Columns.Count, 1 To oLstBxRng.Rows.Count)

    For i = 1 To oLstBxRng.Rows.Count
        blnFound = False
        For j = 1 To oLstBxRng.Columns.Count
            If InStr(1, oLstBxRng.Cells(i, j).Value, strSearchTxt, vbTextCompare) > 0 Then  'Tim kiem chuoi tung dong, cot
                blnFound = True     'Tim thay chuoi
                Exit For
            End If
        Next j
        If blnFound Then
            rCount = rCount + 1     'Luu tong so dòng tìm hay chuoi
            For j = 1 To oLstBxRng.Columns.Count
                sArr(j, rCount) = oLstBxRng.Cells(i, j).Value   'Dua tri tim duoc vao mang, luu hang ngang
            Next j
        End If
    Next i

    'Khai bao lai kich thuoc mang theo so dong tim thay chuoi
    If rCount > 0 Then
        ReDim Preserve sArr(1 To j - 1, 1 To rCount)
    Else    'Khong tim thay dòng nào chua chuoi tk
        ReDim Preserve sArr(1 To j - 1, 1 To 1)
    End If

    If UBound(sArr, 2) > 1 Then     'Mang co nhieu gia tri giong chuoi tim kiem
        sArr = Application.WorksheetFunction.Transpose(sArr)
        oLstBx.List = sArr
    Else    'Mang tra ve chinh xác 1 giá tri tim kiem
        oLstBx.Clear
        oLstBx.AddItem
        For i = 1 To UBound(sArr)
            oLstBx.Column(i - 1, 0) = sArr(i, 1)
        Next i
    End If

    faytLstBxMultiCol = True
    Exit Function

EH_Exit:
    faytLstBxMultiCol = False
    Exit Function
EH:
    MsgBox "Loi: " & Err.Number & vbNewLine & "Noi dung loi: " & Err.Description
    Resume EH_Exit

End Function


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Bổ sung: (sau thời gian mò mẫm thêm :) )
- Thêm phần định các cột kiểu số (number) trong listbox với dấu phân cách hàng ngàn.
- Hàm dùng định dạng cột kiểu số trong listbox với tuỳ chọn nhiều cột.
(Đã đính kèm thêm file có định dạng cột)

Mình cũng có sưu tầm được một Class dùng để "canh lề" từng cột trong listbox riêng biệt (hiện nay Listbox chỉ cho phép canh lề một kiểu, áp dụng cho toàn bộ listbox) nhưng khi chạy thấy nó tải dữ liệu lên listbox chậm nên không đưa vô đây.
Giải thuật của các class này là xử lý từng cột -> từng record -> dùng label tạm để lưu nội dung record, thêm dấu cách vào trước, sau hoặc 2 bên để canh phải, trái, giữa rồi lưu lại nội dung vừa sửa vào listbox. Danh sách có 200 dòng mà nó chạy vòng lập cũng mất ~ 2s nên thôi bỏ qua giải pháp này.

Code hàm formatNumColumnLstBx() :

Mã:
Function formatNumColumnLstBx(sLstBxName As String, sColNumList As String, frm As UserForm, sNumFormat As String)

'----------------------------------------------------------------
'# Muc dích: dùng dinh dang các cot kieu Number trong listbox và dùng trong truong hop dùng Mang (Array) gan du lieu cho LstBox.
'# Tham so:
'#   - sColNumList: danh sách các cot can dinh dang So, cách nhau dau phay ','. Vd: "4,5,6".
'#   - sNumFormat:  kieu dinh dang so. Vd: "#,##0": "#,##0.00" hoac "$#,##0.00"
'----------------------------------------------------------------

    Dim arColNumList As Variant
    Dim lngIndex As Long, i As Integer, intColNum As Integer

    If sColNumList = "" Then Exit Function

    arColNumList = Split(sColNumList, ",")

    With frm.Controls(sLstBxName)
        For i = LBound(arColNumList) To UBound(arColNumList)
            intColNum = Val(Trim(arColNumList(i)))          'Trim de bo loi khoang trang neu co
            If intColNum > .ColumnCount Then Exit Function  'Neu so thu tu cot khong có trong listbox se bao loi
            For lngIndex = 0 To .ListCount - 1
                .List(lngIndex, intColNum - 1) = (Format(Val(.List(lngIndex, intColNum - 1)), sNumFormat))
            Next
        Next i
    End With
End Function


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Cập nhật: 06/06/2019

Đã cập nhật file mới sử dụng code (dùng mảng) của bạn befaint để tối ưu tốc độ tìm kiếm.

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Cập nhật: 09/06/2019

Cập nhật thêm file mới sử dụng code (dùng mảng) của bạn HeSanbi rất ngắn gọn và bẫy lỗi khi range dữ liệu cho listbox không có data.


(***PS: tôi vẫn giữa các file phiên bản khác nhau để các tham khảo code các kiểu)


218600
 

File đính kèm

  • TimKiemListBox_nhieuCot.xlsm
    50.3 KB · Đọc: 427
  • TimKiemListBox_nhieuCot_FormatColumn.xlsm
    56.2 KB · Đọc: 333
  • TimKiemListBox_nhieuCot(dungArray).xlsm
    49.5 KB · Đọc: 302
  • TimKiemListBox_nhieuCot(dungArray)_HeSanbi.xlsm
    44.4 KB · Đọc: 467
Lần chỉnh sửa cuối:
Hi cả nhà,
Cũng bài toán trên nhưng mình không muốn listbox lấy nguyên 1 mảng full Data
-> Chỉ lấy trường "Tên Sản Phẩm" và "Đơn Giá" đưa lên Listbox
Xin được thỉnh giáo các cao nhân
 
Lần chỉnh sửa cuối:
Upvote 0
Anh chạy thử xem nhé. Em chạy thử 20 nghìn dòng thấy sự khác biệt rõ ràng..

Hàm tìm trên array
PHP:
Option Explicit

Public Function find_range_multiCol(ByVal source_range As Range, ByVal string_find As String)
    ''
    ' source_range: vung du lieu goc    '
    ' string_find: chuoi can tim        '
    ' ket qua tra ve mang cac phan tu tim duoc theo string_find.    '
    ''
    Dim arr() As Variant, max_row As Long, max_col As Long, i As Long, j As Long, k As Long
    Dim result() As Variant, item_arr As Variant, kk As Long
    If source_range.Count = 1 Then
        find_range_multiCol = Array(source_range.Value2)
        Exit Function    '// khong xet source_range chi co 1 cell'
    End If
    arr = source_range.Value2   '// chep range vao mang arr'
    max_row = UBound(arr, 1)
    max_col = UBound(arr, 2)
    ReDim result(1 To max_col, 1 To max_row)    '// khai bao kich thuoc mang result, xoay 90 do so voi mang arr'
    string_find = VBA.UCase(string_find)
    If Len(string_find) > 0 Then
        string_find = "*" & string_find & "*"
        For i = 1 To max_row
            For k = 1 To max_col
                item_arr = VBA.UCase(arr(i, k))
                If item_arr Like string_find Then 'neu tim thay'
                    j = j + 1
                    For kk = 1 To max_col
                        result(kk, j) = arr(i, kk)
                    Next kk
                    Exit For    'thoat tim '
                End If
            Next k
        Next i
        If j Then
            ReDim Preserve result(1 To max_col, 1 To j)
            ' xoay mang result'
            find_range_multiCol = transpose_array(result)
        Else
            find_range_multiCol = Array("")
        End If
    Else
        find_range_multiCol = arr
    End If
End Function

Public Function transpose_array(ByVal source_array As Variant)
    ' source_array: mang 2 chieu '
    ' xoay mang source_array 90 do '
    Dim arr As Variant, max_row As Long, max_col As Long, result(), i As Long, j As Long
    arr = source_array
    max_row = UBound(arr, 1)
    max_col = UBound(arr, 2)
    ReDim result(1 To max_col, 1 To max_row)
    For i = 1 To max_row
        For j = 1 To max_col
            result(j, i) = arr(i, j)
        Next j
    Next i
    transpose_array = result
End Function
Code cho sự kiện tìm
PHP:
Private Sub txtChuoiTK_Change()
    Dim strTextSearch As String
    strTextSearch = Me.txtChuoiTK.Value
    'Call faytLstBxMultiCol(oRngLstBx1, strTextSearch, "lstDanhSachVPP", frmSearch)

    Me.lstDanhSachVPP.List = find_range_multiCol(oRngLstBx1, strTextSearch)
End Sub
Mạnh có cảm giác là xài Hàm InStr của bài 1 nó sẻ lọc ra chính xác hơn toán Tử Like hay sao ý ???!!!
 
Upvote 0
Anh xem thử nhé..

Public Function find_range_multiCol(ByVal source_range As Range, ByVal string_find As String, _
Optional ByVal list_col_format_number As String = "0", _
Optional ByVal type_format As String = "#,##0")
...

Tuyệt vời bạn befaint.
Tôi mới test trên dữ liệu 500k dòng, 14 cột (52M). Chỉ có dùng Array mới tải dữ liệu lên là gán xuống listbox được còn cái cách của tôi và dùng ADO nó cũng đơ luôn.
- Qua đây tôi cũng phát hiện là ADO sẽ báo lỗi không tìm thấy cái named range (500k dòng), nếu giảm xuống 65k dòng thì ADO mới thấy Named range mà xử lý (máy dùng Excel 2013, dùng ADO trong Access với 500k dòng thì không gặp lỗi trên). Chẳng lẻ dùng ADO + Named range trong Excel có bị giới hạn? chưa hiểu được vụ này hoặc do tôi code sai cái gì đó. Tôi có đính kèm cái file dữ liệu 500k dòng để mọi người kiểm tra.
- Cũng nói thêm là trong thực tế theo tôi biết sẽ ít khi phải xử lý dữ liệu mấy trăm ngàn dòng trên ứng dụng Font End (FE) nên viêc dùng ADO recordset ở đây không chạy không có nghĩa là nó dở hơn Array nhé các bạn mà tuỳ trường hợp xử lý mà dùng thôi. ADO recordset nó có thế mạnh trong việc thêm, xoá dòng ...mà không cần phải redim, sắp xếp lại bộ nhớ như dùng Mảng (ADO tự xử). Việc lọc, sắp xếp dữ liệu cũng đươc hỗ trợ code xử lý nhanh hơn, ít phức tạp hơn Mảng.

- Link file dữ liệu mẫu dùng Worksheet + Mảng + ADO: SampleData(500k).xlsm
Bài đã được tự động gộp:

Hi cả nhà,
Cũng bài toán trên nhưng mình không muốn listbox lấy nguyên 1 mảng full Data
-> Chỉ lấy trường "Tên Sản Phẩm" và "Đơn Giá" đưa lên Listbox
Xin được thỉnh giáo các cao nhân

Cột nào bạn không muốn hiển thị thì thiết lập độ rộng cột đó = 0 pt trong danh sách Column Widths.
 
Lần chỉnh sửa cuối:
Upvote 0
Mạnh có cảm giác là xài Hàm InStr của bài 1 nó sẻ lọc ra chính xác hơn toán Tử Like hay sao ý ???!!!
Chỗ "chính xác hơn" em chưa hiểu ý anh lắm :(

Trường hợp xử lý nhiều dữ liệu (nhiều lần tìm kiếm) hình như dùng Like phù hợp hơn InStr đó anh..

không muốn hiển thị thì thiết lập độ rộng cột đó = 0 pt trong danh sách Column Widths.
Theo em dồn những cột nào cần lấy vào gần nhau trên bảng tính rồi gán vào là được. Nếu vẫn lấy như anh mà thiết lập 0pt thì vẫn tìm cột đó => kết quả tìm có thể không khớp với từ khóa đã nhập.
 
Upvote 0
Tuyệt vời bạn befaint.
Tôi mới test trên dữ liệu 500k dòng, 14 cột (52M). Chỉ có dùng Array mới tải dữ liệu lên là gán xuống listbox được còn cái cách của tôi và dùng ADO nó cũng đơ luôn.
- Qua đây tôi cũng phát hiện là ADO sẽ báo lỗi không tìm thấy cái named range (500k dòng), nếu giảm xuống 65k dòng thì ADO mới thấy Named range mà xử lý (máy dùng Excel 2013, dùng ADO trong Access với 500k dòng thì không gặp lỗi trên). Chẳng lẻ dùng ADO + Named range trong Excel có bị giới hạn? chưa hiểu được vụ này hoặc do tôi code sai cái gì đó. Tôi có đính kèm cái file dữ liệu 500k dòng để mọi người kiểm tra.
- Cũng nói thêm là trong thực tế theo tôi biết sẽ ít khi phải xử lý dữ liệu mấy trăm ngàn dòng trên ứng dụng Font End (FE) nên viêc dùng ADO recordset ở đây không chạy không có nghĩa là nó dở hơn Array nhé các bạn mà tuỳ trường hợp xử lý mà dùng thôi. ADO recordset nó có thế mạnh trong việc thêm, xoá dòng ...mà không cần phải redim, sắp xếp lại bộ nhớ như dùng Mảng (ADO tự xử). Việc lọc, sắp xếp dữ liệu cũng đươc hỗ trợ code xử lý nhanh hơn, ít phức tạp hơn Mảng.

- Link file dữ liệu mẫu dùng Worksheet + Mảng + ADO: SampleData(500k).xlsm
Bài đã được tự động gộp:



Cột nào bạn không muốn hiển thị thì thiết lập độ rộng cột đó = 0 pt trong danh sách Column Widths.
Cái này chưa chắc ADO đơ hơn Arr nha anh hihih
 
Upvote 0
Hi cả nhà,
Cũng bài toán trên nhưng mình không muốn listbox lấy nguyên 1 mảng full Data
-> Chỉ lấy trường "Tên Sản Phẩm" và "Đơn Giá" đưa lên Listbox
Xin được thỉnh giáo các cao nhân

Quên là trường hợp này bạn có thể dùng ADO để lấy cột dữ liệu nào muốn cũng dễ hơn.
"Select Ten, Gia From [Range]"
Bạn xem bài của bác HLMT:https://www.giaiphapexcel.com/diend...ile-không-mở-kết-nối-adodb-connection.141551/
 
Upvote 0
Mạnh có cảm giác là xài Hàm InStr của bài 1 nó sẻ lọc ra chính xác hơn toán Tử Like hay sao ý ???!!!

Chỗ "chính xác hơn" em chưa hiểu ý anh lắm :(

Trường hợp xử lý nhiều dữ liệu (nhiều lần tìm kiếm) hình như dùng Like phù hợp hơn InStr đó anh..

Tôi thấy ở trường hợp này InStr và Like *#* đều có tác dụng như nhau và tốc độ cũng như nhau thôi chứ không thể nói là 'chính xác' ở đây. Mà để nói đến tốc độ thì chắc phải thử nghiệm và xét thêm tới cái cách mà mỗi hàm thực hiện việc tìm kiếm.
 
Upvote 0
Quên là trường hợp này bạn có thể dùng ADO để lấy cột dữ liệu nào muốn cũng dễ hơn.
"Select Ten, Gia From [Range]"
Bạn xem bài của bác HLMT:https://www.giaiphapexcel.com/diend...ile-không-mở-kết-nối-adodb-connection.141551/
Tks huynh đài , đoạn này:

Dim sArr() As Variant
Set oRngLstBx1 = Range(Range("A2"), Range("A2").End(xlDown).End(xlToRight)) 'Khai báo cho biesn toàn cuc de su dung cho hàm TK
ReDim sArr(1 To oRngLstBx1.Rows.Count, 1 To oRngLstBx1.Columns.Count)
sArr = oRngLstBx1.Value
Me.lstDanhSachVPP.List = sArr

=> Bạn có thể edit lại phần này có thể dùng .list (..,..) = sArr (I,..) để lấy cột trong mảng lên listbox được không?
=> Mong bạn hỗ trợ thêm để mình tham khảo và có thể áp dụng được trong các trường hợp tùy biến data từ mảng lên listbox
(Mình đính kèm lại File của chính bạn để dễ hình dung)
 

File đính kèm

  • TimKiemListBox_nhieuCot.xlsm
    50.3 KB · Đọc: 78
Upvote 0
Tôi thấy ở trường hợp này InStr và Like *#* đều có tác dụng như nhau và tốc độ cũng như nhau thôi chứ không thể nói là 'chính xác' ở đây. Mà để nói đến tốc độ thì chắc phải thử nghiệm và xét thêm tới cái cách mà mỗi hàm thực hiện việc tìm kiếm.
Mạnh ko nói tới tốc độ mà trước đây Mạnh có mấy lần cũng suy nghĩ nên dùng toán tử Like hay InStr .... còn "Chính xác" như thế nào Mạnh thấy trong VBLibrary nó có mô ta 2 cái hàm đó như sau:

Tên hàm:
InStr

Mô tả:
InStr([start, ]string1, string2[, compare])
Tìm chuỗi string2 trong chuỗi string1, tìm từ vị trí start

Tham số:
start
Vị trí tìm
string1
Chuỗi tìm kiếm
string2
Giá trị cần tìm
compare
Chỉ rõ kiểu dữ liệu để so sánh trong quá trình tìm kiếm

Ghi chú:
Dùng cho tham số compare
vbUseCompareOption = –1
Chế độ tùy chọn, VB sẽ tự động lựa lọai dữ liệu thích hợp
vbBinaryCompare = 0
So sánh nhị phân
vbTextCompare = 1
So sánh chuỗi
vbDatabaseCompare = 2
So sánh dữ liệu

Ví dụ:
Mã:
Option Explicit
Private Sub Form_Load()
Dim SearchString, SearchChar, MyPos
    SearchString = "XXpXXpXXPXXP"' String to search in.
    SearchChar = "P"' Search for "P".
' So sanh theo cua text tu vi tri 4
    MyPos = InStr(4, SearchString, SearchChar, 1)' Returns 6.
' So sanh theo Binary
    MyPos = InStr(1, SearchString, SearchChar, 0)' Returns 9.
' So sanh theo Binary, do mac dinh la 0
    MyPos = InStr(SearchString, SearchChar)' Returns 9.
    MyPos = InStr(1, SearchString, "W")' Returns 0.
End Sub

Tên hàm:

Like

Mô tả:
"String1" Like "String2"
So sánh 2 chuỗi cho phép sử dụng biệt ngữ (như dùng ký tự đại diện trong Dos) trị trả về = True nếu tương hợp.

Tham số:
String1, String2: là hai chuỗi ký tự cần so sánh.

Ghi chú:
Hàm Like mặc định cũng là hàm nhạy ký tự, theo thiết lập Option Compare ở form hoặc module

Ví dụ:
Mã:
"abcd" Like "*bcd" = True
"abcd" Like "a?cd" = True
"a1cd" Like "a#cd" = True

Áp dụng cho bài thớt này cứ thử gõ một cái chuỗi nào đó xong xem nó trả về cái gì là biết .... thử các kiểu đi sẻ thấy ....
 
Upvote 0
Mạnh ko nói tới tốc độ mà trước đây Mạnh có mấy lần cũng suy nghĩ nên dùng toán tử Like hay InStr .... còn "Chính xác" như thế nào Mạnh thấy trong VBLibrary nó có mô ta 2 cái hàm đó như sau:

....
Ghi chú:
Dùng cho tham số compare
vbUseCompareOption = –1
Chế độ tùy chọn, VB sẽ tự động lựa lọai dữ liệu thích hợp
vbBinaryCompare = 0
So sánh nhị phân
vbTextCompare = 1
So sánh chuỗi
vbDatabaseCompare = 2
So sánh dữ liệu

................
Ghi chú:
Hàm Like mặc định cũng là hàm nhạy ký tự, theo thiết lập Option Compare ở form hoặc module


Áp dụng cho bài thớt này cứ thử gõ một cái chuỗi nào đó xong xem nó trả về cái gì là biết .... thử các kiểu đi sẻ thấy ....

Vấn đề trên dễ hiểu mà bạn. Việc dùng InStr hay Like còn tuỳ thuộc ý đồ tìm kiếm của bạn thôi.
Đối với ví dụ của tôi, gọi là "tìm kiếm ngay khi gõ" tức là chưa có một chuỗi "chính xác" cần tìm. Vd: gõ chữ "o" là tìm ngay khác với việc chờ gõ đầy đủ chữ "ongke0711" rồi mới tìm Full text. Do đó trong ví dụ của tôi thì dùng InStr hay Like mức độ chính xác như nhau. Hơn nữa bạn xem kỹ sẽ thấy befaint khi dùng Like có dùng hàm "VBA.UCASE" cho cả chuỗi tìm và cần tìm để đảm bảo tính chính xác khi dùng Like với "Option Compare Statement" như tài liệu bạn đã dịch ở trên.
Nói rõ thêm về tài liệu của bạn ở trên. Như bài post trước tôi có nói tìm chính xác và tốc độ còn tuỳ thuộc chế độ tìm kiếm hàm đó dùng kiểu gì: kiểu Binary hay Text. Tìm theo kiểu Binary sẽ nhanh hơn kiểu Text.
- Đối với hàm InStr: Nếu dùng kiểu Binary (vbBinaryCompare) thì nó sẽ xem chữ Hoa và chữ thường là khác nhau (A<a), còn nếu dùng kiểu Text (vbTextCompare) thì chữ Hoa cũng như chữ thường (A =a). Đó là lý do trong ví dụ trên của bạn nó trả về vị trí khác nhau khi thay đổi kiểu tìm kiếm.
- Điều này tương tự như toán tử LIKE: gọi là nhạy ký tự. Còn việc dùng dấu * trước hay sau ký tự cần tìm là tuỳ thuộc ý đồ tìm kiếm.

--------------------
01101111 01101110 01100111 01101011 01100101 00110000 00110111 00110001 00110001
 
Lần chỉnh sửa cuối:
Upvote 0
Các bác cho em hỏi thêm : Định dạng trên File được cập nhật ngày 06/06/2019 là số, còn nếu em muốn chuyển sang date dạng "dd/mm/yyyy" thì cần thay đổi function như thế nào ạ. Tks các bác
 
Upvote 0
Tham gia cải tiến code #1:

PHP:
Option Explicit
Option Compare Text
Dim ArrayData, MaxRow&, MaxCol%, TempArr()
Private Sub txtChuoiTK_Change()
    Call faytLstBxMultiCol(Me.txtChuoiTK.Value, Me.lstDanhSachVPP)
End Sub
Private Sub UserForm_Initialize()
    call ganSourceListbox:Me.txtChuoiTK.SetFocus
End Sub
Private Sub ganSourceListbox()
    With Range("A2")
      MaxCol = .End(xlToRight).Column
      MaxRow = Cells(Rows.Count, .Column).End(xlUp).Row
      ArrayData = .Resize(MaxRow, MaxCol).Value
    End With
    ReDim TempArr(1 To MaxRow, 1 To MaxCol)
    Me.lstDanhSachVPP.List = ArrayData
End Sub
Private Sub faytLstBxMultiCol(strSearchTxt$, _
                                    ListBox As MSForms.ListBox)
    Dim i&, j%, r&
    For i = 1 To MaxRow
      For j = 1 To MaxCol: If ArrayData(i, j) Like "*" & strSearchTxt & "*" Then GoTo AddArr:Next
      If False Then
AddArr:  r = r + 1
        For j = 1 To MaxCol
            TempArr(r, j) = ArrayData(i, j)
            If j = 5 And TempArr(r, j) >= 1000 Then _
              TempArr(r, j) = Format(ArrayData(i, j), "#,##0")
        Next
      End If
    Next
    With ListBox
      .Clear: .AddItem
      If r = 0 Then GoTo EH_Exit
      ReDim result(1 To r, 1 To MaxCol)
      GoSub TranTempArr: .List = result
    End With
EH_Exit: Exit Function
TranTempArr:
  For i = 1 To r: For j = 1 To MaxCol: result(i, j) = TempArr(i, j): Next j, i
Return
End Sub

Private Sub UserForm_Terminate()
  On Error Resume Next
  Erase ArrayData: Erase TempArr
End Sub


thêm tính năng nhập liệu nữa được không ạ (nghĩa là khi kích vào dòng nào sẽ nhập liệu dòng đó xuống bảng tính)?

OT kết hợp các Phương thức của ListBox: Selected , List , ListIndex , ...
Các sự kiện ListBox MouseDown / MouseUp
Ràng buộc các dữ liệu đã lấy trước đó, để tránh Duplicate.
Hoặc Remove ListIndex khi click chọn
Để chọn nhiều thì thuộc tính Multi Select thành True
 
Lần chỉnh sửa cuối:
Upvote 0
Các bác cho em hỏi thêm : Định dạng trên File được cập nhật ngày 06/06/2019 là số, còn nếu em muốn chuyển sang date dạng "dd/mm/yyyy" thì cần thay đổi function như thế nào ạ. Tks các bác

Vấn đề này có nhiều thứ cần bàn nè. Liên quan đến thiết kế Form tìm kiếm.
- Ví trong trong bài #1 là lọc dữ liệu ngay khi gõ 1 ký tự mà đối với tìm kiếm ngày là không phù hợp vì lọc theo ngày là phải lọc nguyên khối ký tự biểu diễn ngày: "09/06/2019". Nếu cột dữ liệu Ngày bạn lưu trong bảng tính Excel dưới dạng TEXT thì nó sẽ tìm được. Nếu lưu đúng định dạng "Date" thì phải xử lý kiểu khác.
- Khi gán cột dạng Date vô mảng (variant) thì nó sẽ tự động chuyển thành dạng Long. Có thể dùng Format ("dd/mm/yyyy") cột ngày trong mảng để hiển thị trên list box cho đúng kiểu nhưng trị nó lưu trong mảng vẫn là dạng Long, Vd: #09/06/2019# --> 43625. Khi đó muốn tìm kiếm ngày thì phải chuyển Date thành Long rồi tìm trong mảng. Bên cạnh đó không thể tìm ngay khi gõ nên phải có textbox riêng để gõ đầy đủ ngày tháng cần tìm rồi mới chạy lệnh tìm kiếm.

Trong thực tế thiết kế ứng dụng tôi cũng chưa thấy ứng dụng nào tìm ngày tháng ngay khi gõ (Find As You Type) mà phải dùng textbox riêng để nhập dữ liệu tìm kiếm. Do vậy ứng dụng FAYT chỉ nên dùng cho các listbox, combox dạng text, number và phải kết hợp với textbox riêng cho dạng Date.

218854

218853
 
Upvote 0
Tham gia cải tiến code #1:

PHP:
Option Explicit
Option Compare Text
Dim ArrayData, MaxRow&, MaxCol%, TempArr()
Private Sub txtChuoiTK_Change()
    Call faytLstBxMultiCol(Me.txtChuoiTK.Value, Me.lstDanhSachVPP)
End Sub
Private Sub UserForm_Initialize()
    call ganSourceListbox:Me.txtChuoiTK.SetFocus
End Sub
Private Sub ganSourceListbox()
    With Range("A2")
      MaxCol = .End(xlToRight).Column
      MaxRow = Cells(Rows.Count, .Column).End(xlUp).Row
      ArrayData = .Resize(MaxRow, MaxCol).Value
    End With
    ReDim TempArr(1 To MaxRow, 1 To MaxCol)
    Me.lstDanhSachVPP.List = ArrayData
End Sub
Private Sub faytLstBxMultiCol(strSearchTxt$, _
                                    ListBox As MSForms.ListBox)
    Dim i&, j%, r&
    For i = 1 To MaxRow
      For j = 1 To MaxCol: If ArrayData(i, j) Like "*" & strSearchTxt & "*" Then GoTo AddArr:Next
      If False Then
AddArr:  r = r + 1
        For j = 1 To MaxCol
            TempArr(r, j) = ArrayData(i, j)
            If j = 5 And TempArr(r, j) >= 1000 Then _
              TempArr(r, j) = Format(ArrayData(i, j), "#,##0")
        Next
      End If
    Next
    With ListBox
      .Clear: .AddItem
      If r = 0 Then GoTo EH_Exit
      ReDim result(1 To r, 1 To MaxCol)
      GoSub TranTempArr: .List = result
    End With
EH_Exit: Exit Function
TranTempArr:
  For i = 1 To r: For j = 1 To MaxCol: result(i, j) = TempArr(i, j): Next j, i
Return
End Sub

Private Sub UserForm_Terminate()
  On Error Resume Next
  Erase ArrayData: Erase TempArr
End Sub

Hay quá bạn HeSanbi. Code ngắn ngọn quá trời.
Code cũ của tôi và befaint chưa có bẫy lỗi Null cho cái range dữ liệu listbox. Báo lỗi "Out of Memory" nếu range không có dữ liệu.
 
Upvote 0
Hay quá bạn HeSanbi. Code ngắn ngọn quá trời.
Code cũ của tôi và befaint chưa có bẫy lỗi Null cho cái range dữ liệu listbox. Báo lỗi "Out of Memory" nếu range không có dữ liệu.
Viết ứng dụng tìm kiếm, code không tối ưu thì mất đi tốc độ xử lý. Và không nên dùng bẫy lỗi.
Nếu ứng dụng tìm kiếm nhiều cột thì bác cần sử dụng thuật toán như: search binary, tìm kiếm nội suy, ... tốc độ xử lý sẽ nhanh hơn có thể 50%
 
Lần chỉnh sửa cuối:
Upvote 0
Chức năng tìm kiếm này có phải tương tự như clip này không ạ?
 
Upvote 0
Web KT
Back
Top Bottom