Thiết lập tiêu đề cho Listbox bằng VBA ? (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

sealand

Thành viên gạo cội
Tham gia
16/5/08
Bài viết
4,883
Được thích
7,688
Giới tính
Nam
Nghề nghiệp
Kế Toán
Mình loay hoay mãi mà không được nên đưa lên đây mọi người hỗ trợ:

Giả sử trên Form có Listbox1. Listbox1 có 2 cột là MaVT và TenVT.

Các lệnh thiết lập listbox như sau:
1/ Lệnh AddItems và List để tạo các dòng trên Listbox1
2/Lệnh cho hiện tiêu đề cột của Listbox là: Listbox1.ColumnHeads=True

Giờ để Listbox có dòng tiêu đề cột 1 là Mã VT và tiêu đề cột 2 là Tên VT ta làm thế nào ?
 
Bây giờ mình làm thử một UserForm có ListView tại máy chỉ có Excel 2007, mình Save As về định dạng 97-2003 (.xls), các bạn có máy sử dụng Excel 2003 tải về và chạy thử xem có lỗi gì không làm ơn chụp hình lỗi lại cho mình xem nhé!

Nhân tiện với ListView khi Add tiêu đề cột, có thể sẽ bực mình vì chuyện độ rộng của từng cột, mình muốn chia sẽ một kinh nghiệm Add nhanh cho nó như sau:

Thông thường mình Add tiêu đề dựa trên một bảng dữ liệu tại sheet nào đó, mình chia độ rộng tại đây đã chuẩn, vì vậy ta lợi dụng điểm này để dùng vòng lặp Add tiêu đề cho nhanh.

Đồng thời cũng nên chú trọng đến Font Size mà chia tỷ lệ cho nó hợp lý:

Thay vì:

[GPECODE=vb] With Sheet1.Range("A1")
ListView1.ColumnHeaders.Add , , .Value, 120
ListView1.ColumnHeaders.Add , , .Offset(, 1).Value, 100
ListView1.ColumnHeaders.Add , , .Offset(, 2).Value, 70
ListView1.ColumnHeaders.Add , , .Offset(, 3).Value, 120
ListView1.ColumnHeaders.Add , , .Offset(, 4).Value, 110
ListView1.ColumnHeaders.Add , , .Offset(, 5).Value, 115
ListView1.ColumnHeaders.Add , , .Offset(, 6).Value, 120
End With
[/GPECODE]

Thì ta chỉ dùng vòng lặp và độ rộng sẳn có của cột làm độ rộng của ListView luôn:

[GPECODE=vb] With Sheet1.Range("A1")
SizeRate = ListView1.Font.Size / .Font.Size
For c = 0 To 6
With .Offset(, c)
ListView1.ColumnHeaders.Add , , .Value, .Width * SizeRate
End With
Next
End With
[/GPECODE]
 

File đính kèm

Upvote 0
Bây giờ mình làm thử một UserForm có ListView tại máy chỉ có Excel 2007, mình Save As về định dạng 97-2003 (.xls), các bạn có máy sử dụng Excel 2003 tải về và chạy thử xem có lỗi gì không làm ơn chụp hình lỗi lại cho mình xem nhé!

Nhân tiện với ListView khi Add tiêu đề cột, có thể sẽ bực mình vì chuyện độ rộng của từng cột, mình muốn chia sẽ một kinh nghiệm Add nhanh cho nó như sau:

Thông thường mình Add tiêu đề dựa trên một bảng dữ liệu tại sheet nào đó, mình chia độ rộng tại đây đã chuẩn, vì vậy ta lợi dụng điểm này để dùng vòng lặp Add tiêu đề cho nhanh.

Đồng thời cũng nên chú trọng đến Font Size mà chia tỷ lệ cho nó hợp lý:

Thay vì:

[GPECODE=vb] With Sheet1.Range("A1")
ListView1.ColumnHeaders.Add , , .Value, 120
ListView1.ColumnHeaders.Add , , .Offset(, 1).Value, 100
ListView1.ColumnHeaders.Add , , .Offset(, 2).Value, 70
ListView1.ColumnHeaders.Add , , .Offset(, 3).Value, 120
ListView1.ColumnHeaders.Add , , .Offset(, 4).Value, 110
ListView1.ColumnHeaders.Add , , .Offset(, 5).Value, 115
ListView1.ColumnHeaders.Add , , .Offset(, 6).Value, 120
End With
[/GPECODE]

Thì ta chỉ dùng vòng lặp và độ rộng sẳn có của cột làm độ rộng của ListView luôn:

[GPECODE=vb] With Sheet1.Range("A1")
SizeRate = ListView1.Font.Size / .Font.Size
For c = 0 To 6
With .Offset(, c)
ListView1.ColumnHeaders.Add , , .Value, .Width * SizeRate
End With
Next
End With
[/GPECODE]

Mình đưa vào máy chỉ có Ex2003, vẫn nạp dữ liệu được bình thường, cũng chẳng cần References gì cả (mở trên máy của một đồng nghiệp chưa từng biết code là gì).

Thế trường hợp nào đưa listview qua máy khác phải References hả Nghĩa, có phải là trường hợp máy cài Office không đầy đủ? Để mình xóa Office cài thiếu lại thử xem và tìm cách khắc phục ra sao.
 
Upvote 0
Thế trường hợp nào đưa listview qua máy khác phải References hả Nghĩa, có phải là trường hợp máy cài Office không đầy đủ? Để mình xóa Office cài thiếu lại thử xem và tìm cách khắc phục ra sao.
Trường hợp máy chưa có cái này: MSCOMCTL.OCX
Lúc đó dù có References gì gì đó cũng không được (có đâu mà tìm)
Vậy thì phải downoad thằng em MSCOMCTL.OCX về cho vào System32 rồi đăng ký thôi anh à!
 
Upvote 0
Trường hợp máy chưa có cái này: MSCOMCTL.OCX
Lúc đó dù có References gì gì đó cũng không được (có đâu mà tìm)
Vậy thì phải downoad thằng em MSCOMCTL.OCX về cho vào System32 rồi đăng ký thôi anh à!

Biết là do không có file OCX đó trong System32 nhưng mình chưa gặp máy không xử dụng được ListVier lần nào nên muốn biết vì sao nó thiếu, có khả năng cái OCX này dùng chung, khi Remove chương trình khác nó lại xóa theo luôn, hoặc cài chương trình khác thì nó lại chép đè một OCX cùng tên lên nhưng không dùng được. Mình đã từng gặp trường hợp đụng hàng giữa VB6 và Acad.
 
Upvote 0
Biết là do không có file OCX đó trong System32 nhưng mình chưa gặp máy không xử dụng được ListVier lần nào nên muốn biết vì sao nó thiếu, có khả năng cái OCX này dùng chung, khi Remove chương trình khác nó lại xóa theo luôn, hoặc cài chương trình khác thì nó lại chép đè một OCX cùng tên lên nhưng không dùng được. Mình đã từng gặp trường hợp đụng hàng giữa VB6 và Acad.

Chính xác là cái màu đỏ đấy anh, máy em cài đầy đủ, update bình thường, thế nhưng khi cài một cái Tool nào đó làm mình mất luôn cả cái Addition luôn!
 
Upvote 0
Xin được các bạn giải thích cụ thể hạn chế của ListView khi nâng cấp office? Mình thích dùng ListView vì sử dụng được chuột phải khi danh sách dài

Cho em hỏi kỹ lại một tí, dùng chuột phải trên ListView là sao anh? Hay anh muốn nói dùng Scroll-Wheel (bánh xe lăn) của chuột?

Nếu dùng bánh xe cho ListBox, ComboBox, anh tham khảo tại bài này:

http://www.giaiphapexcel.com/forum/...ó-thể-sử-dụng-mouse-wheel&p=456996#post456996
 
Lần chỉnh sửa cuối:
Upvote 0
Cho em hỏi kỹ lại một tí, dùng chuột phải trên ListView là sao anh? Hay anh muốn nói dùng Scroll-Wheel (bánh xe lăn) của chuột?

Nếu dùng bánh xe cho ListBox, ComboBox, anh tham khảo tại bài này:

http://www.giaiphapexcel.com/forum/showthread.php?74588-T%E1%BA%B7ng-c%C3%A1c-b%E1%BA%A1n-validation-combo-box-c%C3%B3-th%E1%BB%83-s%E1%BB%AD-d%E1%BB%A5ng-mouse-wheel&p=456996#post456996

Đúng rồi, dùng bánh xe để tìm trong danh sách. Bài bên đó hay nhỉ, nhưng code có vẻ phức tạp. Vậy theo Nghĩa nên dùng LissView hay Listbox? Theo mình thì mình dùng ListView, tối giản code được chừng nào hay chừng đó.
 
Upvote 0
Không biết các máy khác như thế nào, máy anh thiết kế chỉ dùng 1 office 2007 và save as thành 2003, đem file này qua máy chỉ có 2003 vẫn xài listview được, không những thế, anh đã thử trên nhiều máy 2003 ở cơ quan qua mạng nội bộ, máy 2003 nào cũng xài không bị lỗi gì!
Vậy hả anh? Cái này em cũng không biết nữa. Nhà em thì hầu hết các máy đều bị lỗi, chỉ có dùng Office 2007 thì mới không lỗi. Vậy chắc để em nghiên cứu lại vấn đề này, thấy mọi người không có bị lỗi này :(
 
Upvote 0

Thử bới tí bèo xem có ra bọ không.
Tôi muốn xét code ở bài #10 trong đường dẫn trên.
-------------
Có một nguyên tắc chính trong chung sống cộng đồng là: hãy sống và cho phép mọi người khác cũng sống. Mọi tài nguyên, của cải chung anh có thể sử dụng, nhu cầu tới đâu sử dụng tới đó. Nhưng cũng để người khác có quyền như vậy. Nếu anh chiếm công viên, máy rút tiền cho riêng mình thôi là không được.
Windows là hệ đa nhiệm. Cùng lúc có thể hoạt động nhiều ứng dụng. Nếu mọi tài nguyên anh chiếm làm của riêng là không được. Trừ những việc "đặc thù" mà anh làm thì bình thường anh cứ dùng thoải mái, nhưng không được chiếm làm của riêng. Chẳng hạn nếu anh mở Clipboard (của chung) nhưng anh không đóng tức là không có ứng dụng nào có thể sử dụng clipboard được nữa.
Vậy thì hãy dùng, hãy hưởng bao nhiêu anh muốn nhưng sau đó cho phép "người khác" cũng có quyền đó.
------------
Code Hook tôi thấy chưa chuẩn lắm. Windows là hệ đa nhiệm, có nhiều ứng dụng cùng chạy và user nhiều khi "nhẩy" từ ứng dụng này sang ứng dụng khác, làm việc mỗi nơi một lúc. Người lập trình không thể giả thiết là user chạy xong ứng dụng này rồi mới chuyển sang ứng dụng khác. Vd. anh ta có thể nhập một số dữ liệu trong A --> chuyển sang B --> cuộn cửa sổ B tới dữ liệu cần thiết --> copy vào bộ nhớ đệm --> chuyển về A --> dán vào chỗ cần thiết.
Trong code ta có Hook, nhưng nhu cầu của ta không cần tới "cắt cơm, cúp điện" của người khác. Ta cần nắm bắt thời điểm "bánh xe chuột lăn" thì ta được Windows cung cấp, nhưng làm xong những việc cần làm thì phải cho những người cũng như mình - tức cũng cần nắm bắt thời điểm "bánh xe chuột lăn" trong ứng dụng của họ - làm những việc của họ chứ. Hiện thời thì code Hook "cắt cơm, cúp điện" của người khác.
Các bạn hãy mở UserForm --> click vào ListBox --> mở một tài liệu dài bằng notepad --> quay bánh xe chuột trong notepad. Chắc chắn cửa sổ notepad không được cuộn. Vì code trong hook đã "ngắt" không cho Windows thông báo cho notepad sự kiện "bánh xe chuột lăn".
Trong th của ta thì chả có nhu cầu cắt cơm cúp điện notepad, vậy ta phải gọi CallNextHookEx, tức 2 dòng sau cho vào thành chú thích

Mã:
'            LowLevelMouseProc = -1
'            Exit Function

Tóm lại cần chú ý khi viết code. Chỉ trừ những trường hợp cần thiết khi ta cần viết những code "đặc thù", khi ta cần "ngắt" chuỗi thông báo sự kiện không cho những người khác "xếp hàng sau ta" nhận được sự kiện đó. Tức việc "ngắt" là chủ ý vì một lý do "chính đáng" nào đó. Trong mọi trường hợp còn lại thì phải gọi CallNextHookEx để "những người" xếp hàng chờ đợi sự kiện sau ta cũng nhận được thông báo. Nếu không thì "họ" sẽ thao tác sai.
------------
Có một cái buồn cười là nếu ta kích hoạt notepad bên cạnh UserForm rồi quay bánh xe trong notepad thì ListBox trong UserForm được cuộn. Hoặc quay bánh xe lăn ở bất cứ đâu trên màn hình (desktop, Start, khay hệ thống, thanh tác vụ, thanh tiêu đề ... ) thì cũng như thế. Có "chuyện lạ" như thế vì hook trong code được thiết lập cho toàn system, tức "nắm bắt" sự kiện chuột ở mọi nơi - do giá trị 0 của thông số cuối cùng trong hàm SetWindowsHookEx:

Mã:
 lLowLevelMouse = SetWindowsHookEx _
        (WH_MOUSE_LL, AddressOf LowLevelMouseProc, GetAppInstance,[COLOR=#ff0000] [B]0[/B][/COLOR])

Để thiết lập hook chỉ cho ứng dụng của "ta" thì phải truyền "identifier of the thread with which the hook procedure is to be associated". Tức bình thường thì code là:

Mã:
 lLowLevelMouse = SetWindowsHookEx _
        (WH_MOUSE, AddressOf LowLevelMouseProc, 0, [B][COLOR=#ff0000]GetCurrentThreadId[/COLOR][/B])

Đấy là nói về WH_MOUSE còn WH_MOUSE_LL chỉ có thể thiết lập cho toàn system (global).
 
Lần chỉnh sửa cuối:
Upvote 0
Thử bới tí bèo xem có ra bọ không.
Tôi muốn xét code ở bài #10 trong đường dẫn trên.
-------------

Mã:
'            LowLevelMouseProc = -1
'            Exit Function

Tóm lại cần chú ý khi viết code. Chỉ trừ những trường hợp cần thiết khi ta cần viết những code "đặc thù", khi ta cần "ngắt" chuỗi thông báo sự kiện không cho những người khác "xếp hàng sau ta" nhận được sự kiện đó. Tức việc "ngắt" là chủ ý vì một lý do "chính đáng" nào đó. Trong mọi trường hợp còn lại thì phải gọi CallNextHookEx để "những người" xếp hàng chờ đợi sự kiện sau ta cũng nhận được thông báo. Nếu không thì "họ" sẽ thao tác sai.
------------
Có một cái buồn cười là nếu ta kích hoạt notepad bên cạnh UserForm rồi quay bánh xe trong notepad thì ListBox trong UserForm được cuộn. Hoặc quay bánh xe lăn ở bất cứ đâu trên màn hình (desktop, Start, khay hệ thống, thanh tác vụ, thanh tiêu đề ... ) thì cũng như thế. Có "chuyện lạ" như thế vì hook trong code được thiết lập cho toàn system, tức "nắm bắt" sự kiện chuột ở mọi nơi - do giá trị 0 của thông số cuối cùng trong hàm SetWindowsHookEx:

Mã:
 lLowLevelMouse = SetWindowsHookEx _
        (WH_MOUSE_LL, AddressOf LowLevelMouseProc, GetAppInstance,[COLOR=#ff0000] [B]0[/B][/COLOR])

Để thiết lập hook chỉ cho ứng dụng của "ta" thì phải truyền "identifier of the thread with which the hook procedure is to be associated". Tức bình thường thì code là:

Mã:
 lLowLevelMouse = SetWindowsHookEx _
        (WH_MOUSE, AddressOf LowLevelMouseProc, 0, [B][COLOR=#ff0000]GetCurrentThreadId[/COLOR][/B])

Đấy là nói về WH_MOUSE còn WH_MOUSE_LL chỉ có thể thiết lập cho toàn system (global).

Code tại bài đó không phải em viết, mà em cũng chẳng có trình độ để viết, em chỉ ứng dụng và thấy xài được.

Tuy nhiên có một điều em rất đang thắc mắc và e ngại khi dùng code này, nếu không trả nó về FALSE thì những "con lăn" ở các ứng dụng khác xem như "cứng đơ" ra (MakeScrollableWithMouseWheel(ListBox1) = False)

Tính tới trường hợp người ta dùng Ctrl+Break để tạm ngưng sử dụng code thì làm sao trả về cho nó False được!

Vậy theo Thầy, có thể thay đổi hay cải tiến cho trường hợp này không? Hơn thế nữa, có thể xài trên scroll trên "chuột cảm ứng" laptop được như ta thực hiện với ListView được không ạ?

Xin cám ơn Thầy.
 
Upvote 0
Code tại bài đó không phải em viết, mà em cũng chẳng có trình độ để viết, em chỉ ứng dụng và thấy xài được.

Tuy nhiên có một điều em rất đang thắc mắc và e ngại khi dùng code này, nếu không trả nó về FALSE thì những "con lăn" ở các ứng dụng khác xem như "cứng đơ" ra (MakeScrollableWithMouseWheel(ListBox1) = False)

Tính tới trường hợp người ta dùng Ctrl+Break để tạm ngưng sử dụng code thì làm sao trả về cho nó False được!

Vậy theo Thầy, có thể thay đổi hay cải tiến cho trường hợp này không? Hơn thế nữa, có thể xài trên scroll trên "chuột cảm ứng" laptop được như ta thực hiện với ListView được không ạ?

Xin cám ơn Thầy.

Chuyện trước khi đóng UserForm thì phải "gỡ" hook là đương nhiên. Thiết lập hook --> làm việc --> xong việc --> gỡ hook. Nó như là dậy bé: bầy đồ chơi ra --> chơi thỏa thích --> hết chơi --> dọn đồ gọn gàng.
Trong suốt quá trình làm việc thì để con lăn không bị "đơ" khi user chuyển sang ứng dụng khác thì tôi đã viết rồi: 2 dòng ... phải loại bỏ.
Nhưng như thế thì khi user lăn chuột ở ứng dụng khác thì ListBox của ta cũng cuộn theo.
Có thể: trong procedure hook kiểm tra vị trí trỏ chuột --> nếu trỏ chuột nằm trong vùng làm việc của UserForm (client area) thì cuộn ListBox và thêm 2 dòng kia. Code dưới đây chỉ là 1 ví dụ thôi:

[GPECODE=vb]
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type

Private Declare Function ScreenToClient Lib "user32.dll" (ByVal hwnd As Long, ByRef lpPoint As POINTAPI) As Long
Private Declare Function GetClientRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As RECT) As Long
Private Declare Function PtInRect Lib "user32.dll" (ByRef lpRect As RECT, ByVal x As Long, ByVal y As Long) As Long

Function LowLevelMouseProc _
(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim pt As POINTAPI, h As Long, mst As MSLLHOOKSTRUCT, rc As RECT
Static iTopIndex As Integer

On Error Resume Next

If (nCode = HC_ACTION) Then
If wParam = WM_MOUSEWHEEL Then
With oObject
mst = GetHookStruct(lParam)
pt = mst.pt
h = FindWindow("ThunderDFrame", vbNullString)
ScreenToClient h, pt
GetClientRect h, rc
If PtInRect(rc, pt.x, pt.y) Then
If mst.mousedata > 0 Then
.TopIndex = iTopIndex - 1
iTopIndex = .TopIndex
Else
.TopIndex = iTopIndex + 1
iTopIndex = .TopIndex
End If
LowLevelMouseProc = -1
Exit Function
End If
End With
End If
End If

LowLevelMouseProc = _
CallNextHookEx(lLowLevelMouse, nCode, wParam, ByVal lParam)

End Function
[/GPECODE]
 
Upvote 0
Đúng rồi, dùng bánh xe để tìm trong danh sách. Bài bên đó hay nhỉ, nhưng code có vẻ phức tạp. Vậy theo Nghĩa nên dùng LissView hay Listbox? Theo mình thì mình dùng ListView, tối giản code được chừng nào hay chừng đó.

Theo quan điểm của em thì ListBox và ListView đều có các mặt mạnh và hạn chế riêng.

Với ListBox, không kén font nguồn nên không phải convert nếu máy có font đó; nạp list bằng mảng rất nhanh, không cần vòng lặp gì cả (ListBox1.List = Rang("A1:D10").Value), nó có thuộc tính RowSource để liên kết từ Sheet đến ListBox.

Hạn chế của nó là không có Gridline, có tiêu đề cột nhưng chỉ dựa vào RowSource, thanh cuộn (scrollbar) không tự xài scrollwheel của chuột được như các scrollbar khác, hình thức sơ sài, không định dạng được màu trong 1 item của list (định dạng ban đầu thế nào thì ra thế đó thôi); tự điều chỉnh độ cao của nó khi ta đặt chiều cao so với số dòng (ListRow) mà nó hiển thị không đúng (điều này làm cho ta sắp xếp trên Form có thể không đẹp mắt).

Với ListView thì về hình thức đẹp, thanh cuộn mượt mà, sử dụng thanh cuộn kiểu nào cũng ổn (vì thanh cuộn có khả năng co giản để phù hợp với số hàng nên không làm co giản độ cao của ListView); có gridline; định dạng màu sắc, font chữ (lớn, nhỏ, đậm, nghiêng) từng mục trong list; ngoại trừ cột đầu tiên, ta có thể định dạng canh trái, canh phải, canh giữa cho từng cột; có tiêu đề cột và thông qua đó nó như là một nút lệnh, có thể sort theo mục trong cột đó; có nhiều kiểu View cho đối tượng này v.v...

Hạn chế lớn nhất của ListView khiến nhiều người quay lưng với nó là không hỗ trợ font Unicode, vì thế khi sử dụng nó ta cần phải chuyển về một font nào đó mà nó có thể hiển thị được tiếng Việt. Ngoài ra khi Add tiêu đề cũng như nội dung thì không thể làm trực tiếp như ListBox mà phải Add từng mục một. Có thuộc tính RowSource rồi RowSourceType nhưng xem ra không xài được (có thể phải đăng ký nó như thế nào đó mới cho xài, thử bấm vào mục Custom trong properties sẽ thấy).

Cho nên, khi thực hiện bất cứ công việc gì, tùy vào hoàn cảnh và cách bố trí trên form mà ta chọn ListView hay ListBox anh nhé!
 
Lần chỉnh sửa cuối:
Upvote 0
Chuyện trước khi đóng UserForm thì phải "gỡ" hook là đương nhiên. Thiết lập hook --> làm việc --> xong việc --> gỡ hook. Nó như là dậy bé: bầy đồ chơi ra --> chơi thỏa thích --> hết chơi --> dọn đồ gọn gàng.
Trong suốt quá trình làm việc thì để con lăn không bị "đơ" khi user chuyển sang ứng dụng khác thì tôi đã viết rồi: 2 dòng ... phải loại bỏ.
Nhưng như thế thì khi user lăn chuột ở ứng dụng khác thì ListBox của ta cũng cuộn theo.
Có thể: trong procedure hook kiểm tra vị trí trỏ chuột --> nếu trỏ chuột nằm trong vùng làm việc của UserForm (client area) thì cuộn ListBox và thêm 2 dòng kia.

Thầy ơi, nhờ code cải tiến của Thầy mà nó đã trở nên rất tiện ích, em không e ngại gì khi dùng nữa! Cám ơn Thầy rất nhiều!

Nhưng cho em hỏi, với scrollbar của NotePad mình có thể dùng chuột cảm ứng của laptop, nhưng cái này thì chỉ dùng được với scroll wheel thôi. Vậy Thầy có thể cải tiến thêm cho nó được không? (chỉ là "được voi đòi tiên" nhưng nếu có thể thì Thầy cũng thử một lần xem sao Thầy nhé!).

Một lần nữa em cám ơn Thầy rất nhiều.
 
Upvote 0
Theo quan điểm của em thì ListBox và ListView đều có các mặt mạnh và hạn chế riêng.

Với ListBox, không kén font nguồn nên không phải convert nếu máy có font đó; nạp list bằng mảng rất nhanh, không cần vòng lặp gì cả (ListBox1.List = Rang("A1:D10").Value), nó có thuộc tính RowSource để liên kết từ Sheet đến ListBox.

Hạn chế của nó là không có Gridline, có tiêu đề cột nhưng chỉ dựa vào RowSource, thanh cuộn (scrollbar) không tự xài scrollwheel của chuột được như các scrollbar khác, hình thức sơ sài, không định dạng được màu trong 1 item của list (định dạng ban đầu thế nào thì ra thế đó thôi); tự điều chỉnh độ cao của nó khi ta đặt chiều cao so với số dòng (ListRow) mà nó hiển thị không đúng (điều này làm cho ta sắp xếp trên Form có thể không đẹp mắt).

Với ListView thì về hình thức đẹp, thanh cuộn mượt mà, sử dụng thanh cuộn kiểu nào cũng ổn (vì thanh cuộn có khả năng co giản để phù hợp với số hàng nên không làm co giản độ cao của ListView); có gridline; định dạng màu sắc, font chữ (lớn, nhỏ, đậm, nghiêng) từng mục trong list; ngoại trừ cột đầu tiên, ta có thể định dạng canh trái, canh phải, canh giữa cho từng cột; có tiêu đề cột và thông qua đó nó như là một nút lệnh, có thể sort theo mục trong cột đó; có nhiều kiểu View cho đối tượng này v.v...

Hạn chế lớn nhất của ListView khiến nhiều người quay lưng với nó là không hỗ trợ font Unicode, vì thế khi sử dụng nó ta cần phải chuyển về một font nào đó mà nó có thể hiển thị được tiếng Việt. Ngoài ra khi Add tiêu đề cũng như nội dung thì không thể làm trực tiếp như ListBox mà phải Add từng mục một. Có thuộc tính RowSource rồi RowSourceType nhưng xem ra không xài được (có thể phải đăng ký nó như thế nào đó mới cho xài, thử bấm vào mục Custom trong properties sẽ thấy).

Cho nên, khi thực hiện bất cứ công việc gì, tùy vào hoàn cảnh và cách bố trí trên form mà ta chọn ListView hay ListBox anh nhé!

Một bài tổng hợp kinh nghiệm, so sánh giữa ListBox và ListView mà không sách vở nào có được. Cảm ơn Nghĩa!
 
Upvote 0
Thầy ơi, nhờ code cải tiến của Thầy mà nó đã trở nên rất tiện ích, em không e ngại gì khi dùng nữa! Cám ơn Thầy rất nhiều!

Nhưng cho em hỏi, với scrollbar của NotePad mình có thể dùng chuột cảm ứng của laptop, nhưng cái này thì chỉ dùng được với scroll wheel thôi. Vậy Thầy có thể cải tiến thêm cho nó được không? (chỉ là "được voi đòi tiên" nhưng nếu có thể thì Thầy cũng thử một lần xem sao Thầy nhé!).

Một lần nữa em cám ơn Thầy rất nhiều.

Chuột này khác chuột kia thì kể cũng lạ.
Tôi không có chuột cảm ứng và laptop nên muốn nghiên cứu cũng đành chịu.
Tôi viết lại theo cách mới. Thực ra ta không cần theo dõi sự kiện chuột trong toàn system mà chỉ trong phạm vi ứng dụng của ta. Khi chuột được lăn trong cửa sổ của ta thì thông điệp WM_MOUSEWHEEL sẽ được gửi tới hàm cửa sổ của UserForm (window procedure). Vậy chỉ cần "đánh tráo" hàm cửa sổ để xử lý thông điệp WM_MOUSEWHEEL.
Tóm lại ta dùng công nghệ "Window Subclassing" thay cho hook. Với cách này thì lăn chuột trong UserForm hay notepad không ảnh hưởng tới nhau. Code này lại còn đơn giản hơn hook.
Trong code sau và trong tập tin đính kèm là code đầy đủ để chạy. Nhưng tôi thêm 1 dòng (mầu đỏ) để test thông điệp được gửi tới hàm cửa sổ. Vậy sau khi test xong thì dòng này xóa đi cho khỏi nhàm code.
Bạn hãy: Kích hoạt UserForm1 (bấm Button 1) --> chọn 1 mục trong ListBox hoặc nhấn tam giác của Combobox --> lăn chuột vài lần --> copy và dán lên GPE những dòng mà Debug.Print ghi trong cửa sổ Immediate để tôi xem.

Module:

[GPECODE=vb]
Option Explicit

Public Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Public Const GWL_WNDPROC = (-4)
Private Const WM_MOUSEMOVE As Long = &H200
Private Const WM_VSCROLL = &H115
Private Const WM_MOUSEWHEEL = &H20A

Public OldWindowProc As Long
Public obj As Object

Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Static TopIndex As Integer
Dim mousedata As Integer
On Error GoTo end_

If uMsg = WM_MOUSEWHEEL Or uMsg = WM_VSCROLL Then Debug.Print CStr(uMsg)

If uMsg = WM_MOUSEWHEEL And Not obj Is Nothing Then
mousedata = wParam \ 65536
With obj
If mousedata > 0 Then
.TopIndex = TopIndex - 1
TopIndex = .TopIndex
Else
.TopIndex = TopIndex + 1
TopIndex = .TopIndex
End If
Exit Function
End With
End If
end_:
WindowProc = CallWindowProc(OldWindowProc, hwnd, uMsg, wParam, lParam)
End Function

Sub SetWindowProc(ByVal hWin As Long, ByVal DoSet As Boolean)
If DoSet Then
If OldWindowProc = 0 Then
OldWindowProc = SetWindowLong(hWin, GWL_WNDPROC, AddressOf WindowProc)
End If
ElseIf OldWindowProc <> 0 Then
SetWindowLong hWin, GWL_WNDPROC, OldWindowProc
OldWindowProc = 0
End If
End Sub
[/GPECODE]

UserForm:

[GPECODE=vb]
Private hWin As Long

Private Sub ComboBox1_Enter()
Set obj = ComboBox1
End Sub

Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Set obj = Nothing
End Sub

Private Sub ListBox1_Enter()
Set obj = ListBox1
End Sub

Private Sub ListBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Set obj = Nothing
End Sub

Private Sub UserForm_Initialize()
hWin = FindWindow("ThunderDFrame", Me.Caption)
SetWindowProc hWin, True
End Sub

Private Sub UserForm_Terminate()
SetWindowProc hWin, False
End Sub
[/GPECODE]
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Chuột này khác chuột kia thì kể cũng lạ.
Tôi không có chuột cảm ứng và laptop nên muốn nghiên cứu cũng đành chịu.
Tôi viết lại theo cách mới. Thực ra ta không cần theo dõi sự kiện chuột trong toàn system mà chỉ trong phạm vi ứng dụng của ta. Khi chuột được lăn trong cửa sổ của ta thì thông điệp WM_MOUSEWHEEL sẽ được gửi tới hàm cửa sổ của UserForm (window procedure). Vậy chỉ cần "đánh tráo" hàm cửa sổ để xử lý thông điệp WM_MOUSEWHEEL.
Tóm lại ta dùng công nghệ "Window Subclassing" thay cho hook. Với cách này thì lăn chuột trong UserForm hay notepad không ảnh hưởng tới nhau. Code này lại còn đơn giản hơn hook.
Trong code sau và trong tập tin đính kèm là code đầy đủ để chạy. Nhưng tôi thêm 1 dòng (mầu đỏ) để test thông điệp được gửi tới hàm cửa sổ. Vậy sau khi test xong thì dòng này xóa đi cho khỏi nhàm code.
Bạn hãy: Kích hoạt UserForm1 (bấm Button 1) --> chọn 1 mục trong ListBox hoặc nhấn tam giác của Combobox --> lăn chuột vài lần --> copy và dán lên GPE những dòng mà Debug.Print ghi trong cửa sổ Immediate để tôi xem.

Trên máy tính bàn, Window XP, tại cửa sổ Immediate, các thao tác trên ListBox cũng như trên ComboBox đều cho kết quả số giống nhau là 522.

Cám ơn Thầy đã cải tiến. Để em thử với laptop xem sao.

===============================

Kiểm tra trên Window 7, máy laptop, thao tác trên chuột ảo, kết quả thật "quái chiêu" là TRÊN CẢ TUYỆT VỜI!!!

Em không ngờ, Thầy không có máy tính để thử scroll ảo của mousepad cảm ứng, vậy mà Thầy vẫn thành công! Quá siêu mà!

Tại đây cũng cho ra giá trị 522 Thầy nhé!

Cám ơn Thầy rất rất nhiều!
 
Lần chỉnh sửa cuối:
Upvote 0
Tuyệt!
Mình thử cũng vậy, khi lăn chuột thì được các số 522, khi bấm các nút tam giác thì không in ra cửa sổ Immediate gì cả
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
522
Thử xong, xin mạn phép cho thêm lệnh này
Mã:
Private Sub UserForm_Activate()
    ListBox1.SetFocus
End Sub
 
Upvote 0
Thầy ơi, cho em hỏi, Thầy cần kiểm tra cái này với mục đích gì?

copy và dán lên GPE những dòng mà Debug.Print ghi trong cửa sổ Immediate để tôi xem.

Và sau khi em đã thử với 2 máy tính, kết quả chỉ cho ra đúng 1 con số là 522, vậy nó mang ý nghĩa gì ạ?
 
Lần chỉnh sửa cuối:
Upvote 0
Thầy ơi, cho em hỏi, Thầy cần kiểm tra cái này với mục đích gì?



Và sau khi em đã thử với 2 máy tính, kết quả chỉ cho ra đúng 1 con số là 522, vậy nó mang ý nghĩa gì ạ?

522 thì chuẩn rồi - Private Const WM_MOUSEWHEEL = &H20A = 522
522 là thông điệp WM_MOUSEWHEEL
 
Upvote 0
522 thì chuẩn rồi - Private Const WM_MOUSEWHEEL = &H20A = 522
522 là thông điệp WM_MOUSEWHEEL

Cho em hỏi thêm, Trong Hàm có dòng này:

mousedata = wParam \ 65536

Với số đó có nghĩa là gì ạ? Hay là chia theo số hàng của sheet? Nếu vậy thì Excel 2007 có phải chia lại không?
 
Upvote 0
Web KT

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

Back
Top Bottom