PDA

View Full Version : Nhờ hoàn thiện form CHỌN dữ liệu



tuan_anhbm
24-04-10, 09:22 AM
Chào các bạn !
Tôi có làm 1 form CHỌN dữ liệu như trong file đính kèm. Mục đích là để hỗ trợ việc chọn dữ liệu trong 1 danh sách hàng nghìn dòng – từ sheet này nạp qua 1 sheet khác - được thuận tiện (ví dụ này chỉ tượng trưng vài công việc).
Trong đó, một số yêu cầu đã tạm giải quyết như: Tạo form, đưa list dữ liệu cần chọn vào form, viết code cho các nút “Chọn”, “Thoát”, thử nghiệm thấy tạm ổn.
Có 3 vấn đề đang bí mò chưa ra --> cần giúp đỡ:
1. Thiết lập định dạng cho danh sách dữ liệu trong form để có đường kẻ dọc, ngang phân cách các cột và dòng công việc.
2. Trong 1 Combobox mà tôi đã tạo sẵn, có đặt các Item (= nhóm công việc: AA, AB, AC, AD…). Viết code thế nào để khi ta chọn vào 1 trong các Item này thì dữ liệu trong form tự động nhảy đến dòng công việc có nhóm ký tự đầu tiên mà ta vừa chọn đó ?
3. Làm sao để cho form hiển thị trên sheet này mà list dữ liệu vẫn lấy được từ sheet khác (cụ thể là tôi muốn form hiển thị trên SheetA mà list công việc hiển thị trong đó là từ sheetB, dữ liệu lấy từ B nạp vào A).
Bí quá thử dùng “tối kiến”:
Application.ScreenUpdating = False
Application.ScreenUpdating = True
Nhưng khi đó trông code cứ loạn cả lên, ngoài ra lại nảy sinh rắc rối như không chọn được Item trong Combobox…
Rất mong các bạn bỏ chút thời gian xem giúp tôi đi nốt chặng đường gian khó.

Hai Lúa Miền Tây
24-04-10, 11:10 AM
Chào các bạn !
Tôi có làm 1 form CHỌN dữ liệu như trong file đính kèm. Mục đích là để hỗ trợ việc chọn dữ liệu trong 1 danh sách hàng nghìn dòng – từ sheet này nạp qua 1 sheet khác - được thuận tiện (ví dụ này chỉ tượng trưng vài công việc).
Trong đó, một số yêu cầu đã tạm giải quyết như: Tạo form, đưa list dữ liệu cần chọn vào form, viết code cho các nút “Chọn”, “Thoát”, thử nghiệm thấy tạm ổn.
Có 3 vấn đề đang bí mò chưa ra --> cần giúp đỡ:
1. Thiết lập định dạng cho danh sách dữ liệu trong form để có đường kẻ dọc, ngang phân cách các cột và dòng công việc.
2. Trong 1 Combobox mà tôi đã tạo sẵn, có đặt các Item (= nhóm công việc: AA, AB, AC, AD…). Viết code thế nào để khi ta chọn vào 1 trong các Item này thì dữ liệu trong form tự động nhảy đến dòng công việc có nhóm ký tự đầu tiên mà ta vừa chọn đó ?
3. Làm sao để cho form hiển thị trên sheet này mà list dữ liệu vẫn lấy được từ sheet khác (cụ thể là tôi muốn form hiển thị trên SheetA mà list công việc hiển thị trong đó là từ sheetB, dữ liệu lấy từ B nạp vào A).
Bí quá thử dùng “tối kiến”:
Application.ScreenUpdating = False
Application.ScreenUpdating = True
Nhưng khi đó trông code cứ loạn cả lên, ngoài ra lại nảy sinh rắc rối như không chọn được Item trong Combobox…
Rất mong các bạn bỏ chút thời gian xem giúp tôi đi nốt chặng đường gian khó.

1.) Bạn chuyển sang dùng ListView cho nó đẹp --> Bạn tự nghiên cứu nhé.
2.) Chuyển combobox sang TextBox để lọc list
3.) Khi mở form, đưa dữ liệu nguồn cho List

Bạn xem file nhé.

tuan_anhbm
24-04-10, 02:16 PM
Cảm ơn Domfootwear nhiều nha.
Bước đầu test qua thấy đúng ý rồi đấy.
Đang tìm hiểu ý nghĩa code của bạn, có gì hồi âm sau nhé.
---------------------------------------------------------------------------
1.) Bạn chuyển sang dùng ListView cho nó đẹp --> Tôi sẽ tự nghiên cứu.
2.) Chuyển combobox sang TextBox để lọc list --> Cái này rất hay!
3.) Khi mở form, đưa dữ liệu nguồn cho List --> tôi bỏ "Sheets("b").Select" trong sheetA là được.
----------------------------------------------------------------------------
Đây quả là những bài học rất hữu ích với những người tự học như tôi.

tuan_anhbm
24-04-10, 04:14 PM
Domfootwear ơi, bạn xem sửa giúp mình chút nữa nhé:
Bạn dùng sự kiện Private Sub TextBox1_Change() để lọc ra danh sách duy nhất trong Listbox là rất hay và nhanh với dữ liệu nhỏ cỡ 100 dòng, nhưng khi tôi thử với 1 dữ liệu lớn cỡ vài ngàn dòng thì thấy nó rất chậm. Có lẽ do phải tính toán để lọc danh sách duy nhất (AutoFilter và For Each / Next) ? Do chỉ hiểu sơ ý đồ code của bạn chứ chưa hiểu rõ từng câu từng chữ nên chưa tự tùy biến được.
Nhờ bạn chỉnh code sao cho: Khi gõ 1 hoặc 2 ký tự đầu (vd: A, hay AB) thì nó tự NHẢY đến dòng cv đầu tiên chứa ký tự đó (không cần trích lọc duy nhất). Do danh sách cv đã được Sort sãn nên việc này cũng sẽ cho kết quả gần giống như trích lọc duy nhất. Tôi nghĩ nếu được vậy sẽ nhanh hơn.
Nhờ Domfootwear và các bạn xem giúp.
Cảm ơn nhiều.

ndu96081631
25-04-10, 11:43 AM
Domfootwear ơi, bạn xem sửa giúp mình chút nữa nhé:
Bạn dùng sự kiện Private Sub TextBox1_Change() để lọc ra danh sách duy nhất trong Listbox là rất hay và nhanh với dữ liệu nhỏ cỡ 100 dòng, nhưng khi tôi thử với 1 dữ liệu lớn cỡ vài ngàn dòng thì thấy nó rất chậm. Có lẽ do phải tính toán để lọc danh sách duy nhất (AutoFilter và For Each / Next) ? Do chỉ hiểu sơ ý đồ code của bạn chứ chưa hiểu rõ từng câu từng chữ nên chưa tự tùy biến được.
Nhờ bạn chỉnh code sao cho: Khi gõ 1 hoặc 2 ký tự đầu (vd: A, hay AB) thì nó tự NHẢY đến dòng cv đầu tiên chứa ký tự đó (không cần trích lọc duy nhất). Do danh sách cv đã được Sort sãn nên việc này cũng sẽ cho kết quả gần giống như trích lọc duy nhất. Tôi nghĩ nếu được vậy sẽ nhanh hơn.
Nhờ Domfootwear và các bạn xem giúp.
Cảm ơn nhiều.
Do dùng For... Next để nạp dữ liệu vào listbox nên chậm là phải rồi
Một cách khác tôi nghĩ sẽ cho tốc độ nhanh hơn, đó là dùng AdvancedFilter lọc dữ liệu ra 1 vùng phụ rồi lấy vùng này là RowSource cho list ---> Vậy là khỏi cần vòng lập nào!
Đề xuất code cho ListBox như sau:


Private Sub TextBox1_Change()
Application.ScreenUpdating = False
CongViecDuocChon.RowSource = ""
If Len(Trim(TextBox1.Value)) = 0 Then Exit Sub
With Sheet12.Range(Sheet12.[A4], Sheet12.[B65536].End(xlUp))
.Parent.Range("G:H").Clear
.Parent.Range("D2").Value = TextBox1.Value & "*"
.AdvancedFilter 2, .Parent.Range("D1:D2"), .Parent.Range("G1")
End With
With Sheet12.Range(Sheet12.[G3], Sheet12.[H65536].End(xlUp))
CongViecDuocChon.RowSource = .Parent.Name & "!" & .Cells.Address
End With
Application.ScreenUpdating = True
End SubĐã thí nghiệm với dữ liệu 10.000 dòng ---> Tốc độ rất nhanh
Chú ý tại Sheet B, các cột D, G, H là các vùng phụ (bạn có thể thay đổi nếu có nhu cầu khác)

tuan_anhbm
26-04-10, 08:00 AM
Cảm ơn thầy NDU nhiều.
Tôi đã thử nghiệm với dữ liệu lớn thấy cho tốc độ tuyệt vời ! (không hiểu sao khi copy nối tiếp dữ liệu để thí nghiệm thì chạy ngon, còn dán đè 1 dữ liêu mới lên DL cũ thì bị lỗi ?).
Tuy vậy tôi vẫn muốn thử nghiệm theo 1 phương án khác mà chắc có lẽ nó sẽ cho tốc độ nhanh hơn nữa :
Các bước mà tôi hình dung như sau (nhưng chưa chuyển qua ngôn ngữ VBA được):
- Tìm bên SheetB, mảng A5:A... (mảng dữ liệu nguồn), giá trị tìm kiếm = TextBox1.Value
- Giả như sẽ tìm được ô chứa mã hiệu cv đầu tiên có ký tự trùng với giá trị trong TextBox --> đặt ô tìm được = R, ta sẽ có chỉ số dòng đầu: dongdau = R.Row; dòng cuối: dongcuoi = Sheet12.[a65536].End(xlUp).Row
- Nạp giá trị mới cho Listbox: CongViecDuocChon.RowSource = "b!A” & dongdau & “:B" & dongcuoi
Lúc này danh sách cv trong Listbox sẽ bắt đầu từ cv đầu tiên tìm thấy trong mảng DL nguồn trở xuống dưới.
Do danh sách cv đã được Sort sẵn nên sẽ cho kết quả gần giống như kiểu trích lọc duy nhất.
Nhờ thầy NDU xem nó có khả thi không ? Nếu được nhờ thầy giúp thử p/a này xem sao.

tuan_anhbm
26-04-10, 09:16 AM
Chào các bạn, tôi đã làm được rồi (theo ý tưởng đã nêu ở bài trên):


Private Sub TextBox1_Change()
Dim Sh As Worksheet, Rng As Range, sRng As Range
Dim R As String
R = TextBox1.Value
If R = "" Then Exit Sub
Set Sh = Sheets("B")
Set Rng = Sh.Range(Sh.[a5], Sh.[a65536].End(xlUp))
Set sRng = Rng.Find(R, , xlFormulas, xlPart)
If sRng Is Nothing Then Exit Sub
dongdau = sRng.Row
dongcuoi = Sheets("B").[a65536].End(xlUp).Row
CongViecDuocChon.RowSource = ""
CongViecDuocChon.RowSource = "b!A" & dongdau & ":B" & dongcuoi
End Sub
Mời các bạn xem qua, có gì cho ý kiến.
Cảm ơn rất nhiều các bạn đã quan tâm, giúp đỡ.

ndu96081631
26-04-10, 09:30 AM
Chào các bạn, tôi đã làm được rồi (theo ý tưởng đã nêu ở bài trên):


Private Sub TextBox1_Change()
Dim Sh As Worksheet, Rng As Range, sRng As Range
Dim R As String
R = TextBox1.Value
If R = "" Then Exit Sub
Set Sh = Sheets("B")
Set Rng = Sh.Range(Sh.[a5], Sh.[a65536].End(xlUp))
Set sRng = Rng.Find(R, , xlFormulas, xlPart)
If sRng Is Nothing Then Exit Sub
dongdau = sRng.Row
dongcuoi = Sheets("B").[a65536].End(xlUp).Row
CongViecDuocChon.RowSource = ""
CongViecDuocChon.RowSource = "b!A" & dongdau & ":B" & dongcuoi
End SubMời các bạn xem qua, có gì cho ý kiến.
Cảm ơn rất nhiều các bạn đã quan tâm, giúp đỡ.
Giải thuật này đã từng được áp dụng cho các name động để rút gọn danh sách (trong VBA ta dùng Find còn công thức thì người ta sẽ dùng MATCH để tìm)
Tuy nhiên, điều tiên quyết để giải thuật có thể áp dụng thành công là: DỮ LIỆU PHẢI ĐƯỢC SORT TRƯỚC ---> Bạn lưu ý nha!
Ngoài ra, Find method khi tìm theo kiểu không chính xác (xlPart) thì cũng có nhược điểm, ví dụ: bạn gõ chữ A thì nó sẽ tìm theo kiểu *A*, tức AAA111 hay BBAA111 hay CCA111 (miển có chữ A) nó đều tìm tuốt ---> Trong 1 số trường hợp cụ thể bạn sẽ thấy nó không hẳn đáp ứng được nhu cầu
Tóm lại: Để dò tìm theo kiểu tra từ điển thì:
- Dữ liệu phải được sort trước
- Dùng hàm MATCH tìm vị trí đầu tiên (chứ không phải Find method)
- Dùng hàm COUNTIF để tính xem có bao nhiêu dòng chứa giá trị tìm
- Xác định Range dựa vào 2 kết quả trên
- Nạp Range Address vào RowSource của Listbox (hoặc ComboBox)
-----------------------------------
Bạn nghiên cứu tiếp xem!

tuan_anhbm
26-04-10, 09:44 AM
Cảm ơn thầy.
Đã sơ qua hiểu được những chỉ dẫn rất giá trị.
Tôi sẽ cố gắng cải tiến, hoàn thiện theo hướng đó.