thuộc tính nào dùng để lấy ra giá trị của dòng đang được chọn trong listbox ?

Lập trình thì tới bước cuối cùng mới sử dụng tới tip & trick. Cái gì cũng có cách giải quyết theo phương pháp chuẩn của nó.

Như tôi nói ở trên, bài toán của bạn chắc chắn là bài toán chuẩn rồi (vì kiểu gì đám Microsoft coders trước kia cũng phải đụng tới).

Và nó được giải quyết thế này:

Conventional Visual Basic wisdom states that in order to gather
the selected items from a multi-select ListBox, you should loop
through all the items and test the Selected property. As with
all loops, however, this can potentially bog down slower CPU's.
As a much faster and more elegant alternative, you can use the
SendMessage() API function instead.

As you probably know, this function lets you send a message to
one or more windows. The declaration statement conforms to the
following syntax:

Mã:Sao chép.
Private Declare Function SendMessage Lib "user32" _ Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg _ As Long, ByVal wParam As Long, lParam As Any) As Long

Since we want to gather the listbox's selected items, we'll
send the LB_GETSELITEMS constant in the wMsg argument, which
you declare like so:

Mã:Sao chép.
Private Const LB_GETSELITEMS = &H191

In essence, the LB_GETSELITEMS message fills an array with the
index numbers of all the selected items. As a result, you must
pass two additional arguments with SendMessage(). The first
argument should contain the maximum number of selected items.
To retrieve this value, you can simply use the listbox's
SelCount property. The second argument should hold the array
variable you want to fill with index values. The following
example shows how you might use this function:

Mã:Sao chép.
Dim ItemIndexes() As Long, x As Integer, iNumItems As Integer iNumItems = ThisBox.SelCount If iNumItems Then ReDim ItemIndexes(iNumItems - 1) SendMessage ListBox1.hwnd, LB_GETSELITEMS, iNumItems, _ ItemIndexes(0) End If For x = 0 To iNumItems - 1 MsgBox ListBox1.List(ItemIndexes(x)) Next x

After being passed to the SendMessage function, iNumItems holds
the total number of selected items, and the ItemIndexes array
holds the selected item index values. Notice, that you must
pass a pointer to the ItemIndexes array, and not the array
itself. Thus, we passed ItemIndexes(0) into the SendMessage
function, not ItemIndexes().
Nhấp chuột vào đây để mở rộng...

99% các câu hỏi liên quan tới lập trình (xử lý kỹ thuật) đều có thể giải quyết theo bước:
- Dịch câu hỏi sang tiếng anh
- Sử dụng Google

Ngoài "câu chuyện" của bạn là: gather/get/remove multi-selected VB listbox items như đã nói ở trên, bạn còn phải quan tâm tới limits của standard listbox (vì thế ít khi PM xịn sử dụng listbox cho dữ liệu nhiều). Nếu ko bạn sẽ phải xử lý chuyện > 64k dữ liệu được load lên standard listbox thế nào? Làm thế nào để trong nháy mắt có thể load bụp 1 cái 1tr bản ghi lên listbox :p, v.v...

Ngoài ra, bạn còn phải nghĩ tới việc duplicate copy from a listbox to another, remove selected listbox items, etc... (all w/ fastest way). Tất cả những việc như vậy đang đợi bạn và tôi chỉ có thể gợi ý đến đây mà thôi.
hoa35ktxd đã viết:
Trời, tôi muốn tìm được cách làm đơn giản trực tiếp trong VBA thôi, không đao to búa lớn làm gì vì cồng kềnh phức tạp.
Còn muốn xử lý như bạn nói thì tôi dùng .Net cho đơn giản hơn nhiều, chả phải DLL gì cho phức tạp và tốn công.
Thân.
Nhấp chuột vào đây để mở rộng...

Vấn đề là con người khi chọn đường đi thì cần phải biết đường đó có đi được hay ko. Khi đã chứng minh là có tảng đá to lù lù trước mặt rồi thì nếu mà còn cố đi tiếp người ta gọi là....

Chúng ta phải biết cách học giải quyết vấn đề (nếu tôi làm VBA thật thì chắc chỉ khoảng 10 phút là tôi biết cái đó có làm được trên VBA hay ko. VBA hay Excel là đồ của M$ làm ra, chỉ cần vào msdn seach 1 lúc ko thấy đáp ứng thì về mặt chính thức là ko có cách nào xử lý. Mà khi M$ tạo ra nó nói đó là limited thì tốt nhất đừng tiếp tục làm tiếp vấn đề đó vì cái đó gọi là "biết là ko được mà cứ làm").

Trong quá trình giải quyết vấn đề, khi làm ko được thì tìm, tìm không được thì làm cách khác chứ ko đi tiếp vào ngõ cụt (google & microsoft mà "solution not found" thì tớ cam đoan là chả có ai trên GPE làm được).

Bạn khi làm việc với Excel, với VBA thì phải hiểu là cái đó rất hạn chế. Properties và Methods của controls hạn chế như vậy thì tìm tiếp làm gì. Thế nên người ta mới gọi là VBA. Excel chỉ mạnh ở cái bảng tính với dữ liệu nhỏ thôi (mở files 500,000 dòng thì biết ngay) và VBA chỉ là 1 phần nhỏ script language bổ sung vào mà thôi.
hoa35ktxd đã viết:
Ở đây thật đáng tiếc vì VBA không có cái thuộc tính SelCount
Nhấp chuột vào đây để mở rộng...

Để lấy được SelCount của ListBox thì bạn làm như sau:

Đoạn này để ở module nào đó
Mã:Sao chép.
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Public Const LB_GETSELCOUNT = &H190

Mã:Sao chép.
Public Function fnListBox_SelCount(objListBox as ListBox) as Long ' get the number of selected items in the listbox fnListBox_SelCount = SendMessage(objListBox.hWnd, LB_GETSELCOUNT, 0&, ByVal 0&) End Function

Sử dụng:

...
'// iNumItems = Listbox1.SelCount
iNumItems = fnListBox_SelCount(Listbox1)
....

Tớ vừa tra trên API Declaration và thấy hằng số LB_GETSELCOUNT nên đoán là nó sẽ chạy vì nếu viết như thế này thì nó sẽ lấy được tương tự như thuộc tính .ListCount

Mã:Sao chép.
' get the number of items in the source list numItems = SendMessage(objListBox.hWnd, LB_GETCOUNT, 0&, ByVal 0&)

Nếu đoạn code trên mà thực hiện được thì coi như bài toán đã được giải quyết.
(Trước đó phải lấy được handle của objListBox đã vì trong VBA thì Listbox là Windowless control. Sử dụng hàm FindWindowEx)

Về mặt lý thuyết mà nói thì mọi ActiveX Controls trong HĐH Windows đều là các Window và đều có thể mở rộng nó thành những Window chuẩn. VBA chẳng qua cũng dùng tương tự như VB6 nhưng nó làm hẹp phạm vi đi mà thôi. Còn những gì trước kia phải làm bằng APIs với VS6 thì nay ông M$ biến nó thành bộ .NET Framework với hàng trăm nghìn tính năng được tích hợp theo từng object và trở thành thư viện để cho chúng ta dùng 1 cách dễ dàng.
Cảm ơn bạn hai2hai đã dành nhiều thời gian cho vấn đề của tôi.
Sau 1 hồi mày mò, tôi cũng đã làm được cái tôi cần, không dùng API, chỉ thông qua các thao tác khi sử dụng để lưu lại những Item được chọn. Có vẻ hơi dài dòng nhưng tôi thấy hiệu quả.
Các bạn tham khảo và cho ý kiến nhé, có thể vẫn còn sót tình huống.
PHP:Sao chép.
Dim Str As String 'Chuỗi lấy danh sách các Item được chọn Dim Mdn As Boolean 'MouseDown Dim Mm As Boolean ' MouseMove Dim ShiftK As Integer Dim StID As Long 'Vị trí ban đầu Dim BotID As Long, TopID As Long 'Vị trí đầu, cuối danh sách Private Sub L1_Change() If ShiftK = 0 Then 'Không có phím điều khiển nào được nhấn If Not Mm Then 'Không kéo rê chuột StID = L1.ListIndex Str = "(" & L1.ListIndex & ")" Else 'Kéo rê chuột LienTuc End If ElseIf ShiftK = 1 Or ShiftK = 3 Then 'Bâìm Shift hoãòc Ctrl+Shift LienTuc ElseIf ShiftK = 2 Then 'Bấm Ctrl If Not Mm Then 'Không kéo rê chuột If L1.Selected(L1.ListIndex) Then StID = L1.ListIndex If InStr(Str, "(" & L1.ListIndex & ")") <> 0 Then Str = Replace(Str, "(" & L1.ListIndex & ")", "") Else If Mdn Then 'Chọn bằng chuột Str = Str & "(" & L1.ListIndex & ")" Else 'Chọn bằng phím Str = "(" & L1.ListIndex & ")" End If End If Else 'Kéo rê chuột LienTuc End If End If Me.Caption = Str End Sub Private Sub LienTuc() Str = "" If StID > L1.ListIndex Then BotID = StID: TopID = L1.ListIndex Else BotID = L1.ListIndex: TopID = StID End If For I = TopID To BotID Str = Str & "(" & I & ")" Next End Sub Private Sub L1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) ShiftK = Shift End Sub Private Sub L1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) ShiftK = Shift End Sub Private Sub L1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) ShiftK = Shift Mdn = True End Sub Private Sub L1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) ShiftK = Shift If Mdn Then Mm = True End Sub Private Sub L1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) ShiftK = Shift Mm = False: Mdn = False End Sub Private Sub UserForm_Activate() Dim Lst() As Long For I = 1 To 1000 L1.AddItem I Next End Sub