EDDYKT
asked on
speed up additem
I have over 66000 text line (reading from ASCII file) wants to display on the list box and each line is variable length.
The following is my routine.
Public Sub AddToListBox()
Dim Mess() As String, I As Long, Counter As Long
Dim Tmp As String, Length As Long, ListHwnd As Long, TextSize As Long
On Error Resume Next
LstMessages.Clear
LstMessages.SetFocus
ListHwnd = GetFocus()
LstMessages.Visible = False
DoEvents
Me.ScaleMode = 3
Mess = Split(Messages, vbCrLf) ' contains all lines with vbcrlf delimiter
Counter = UBound(Mess)
Call LockWindowUpdate(ListHwnd)
For I = 1 To Counter + 1
Tmp = Mess(I - 1)
SendMessage ListHwnd, LB_ADDSTRING, 0, ByVal Tmp
Length = Me.TextWidth(Tmp) + 20
TextSize = IIf(Length > TextSize, Length, TextSize)
Next
Call LockWindowUpdate(0&)
SendMessage ListHwnd, LB_SETHORIZONTALEXTENT, TextSize, 0&
LstMessages.Visible = True
Me.ScaleMode = 1
End Sub
Even I replace the line
SendMessage ListHwnd, LB_ADDSTRING, 0, ByVal Tmp
with
LstMessages.additem Tmp
It tooks over 2 min on my computer (800MHz PIII).
Is any way that it can speed up the additem or using sendmessage.
I would not like to use any third party listbox.
Any idea?
The following is my routine.
Public Sub AddToListBox()
Dim Mess() As String, I As Long, Counter As Long
Dim Tmp As String, Length As Long, ListHwnd As Long, TextSize As Long
On Error Resume Next
LstMessages.Clear
LstMessages.SetFocus
ListHwnd = GetFocus()
LstMessages.Visible = False
DoEvents
Me.ScaleMode = 3
Mess = Split(Messages, vbCrLf) ' contains all lines with vbcrlf delimiter
Counter = UBound(Mess)
Call LockWindowUpdate(ListHwnd)
For I = 1 To Counter + 1
Tmp = Mess(I - 1)
SendMessage ListHwnd, LB_ADDSTRING, 0, ByVal Tmp
Length = Me.TextWidth(Tmp) + 20
TextSize = IIf(Length > TextSize, Length, TextSize)
Next
Call LockWindowUpdate(0&)
SendMessage ListHwnd, LB_SETHORIZONTALEXTENT, TextSize, 0&
LstMessages.Visible = True
Me.ScaleMode = 1
End Sub
Even I replace the line
SendMessage ListHwnd, LB_ADDSTRING, 0, ByVal Tmp
with
LstMessages.additem Tmp
It tooks over 2 min on my computer (800MHz PIII).
Is any way that it can speed up the additem or using sendmessage.
I would not like to use any third party listbox.
Any idea?
are you sure that you need 66000 lines in a listbox? what is the purpose of this listbox?
IMHO you have about 65,800 items too many. It is time to re-design.
Anthony
Anthony
Gotta agree with everyone here...design needs reworking, not the functionality or code. Can these items be further separated into groups? Can they be fed to a database for quicker retrieval later?
I must to agree too.
At this point, API is the fastest you can reach.
At this point, API is the fastest you can reach.
I agree as well: Load the first (let's say) 200, then use a background thread to continue the loading of the rest.
D'Mzzl!
RoverM
D'Mzzl!
RoverM
Rather than jump on the bandwagon...oh, what the heck...image the poor use who has to scroll through 66,000 items to find the desired one.
How about loading everything into a multi-line textbox and whenever the user clicks, determine the current location to determine the "selected" line?
Regardless of your implementation, how can you expect 66,000 lines of variable-length text to be split into small pieces and gathered together quickly? String parsing is usually the slowest thing a computer does.
How about loading everything into a multi-line textbox and whenever the user clicks, determine the current location to determine the "selected" line?
Regardless of your implementation, how can you expect 66,000 lines of variable-length text to be split into small pieces and gathered together quickly? String parsing is usually the slowest thing a computer does.
My listbox can load only 32768 items.
If speed is important, you don't want to use Me.TextWidth or Iif.
GetFocus() - this was needed in VB1, now listboxes have hWnd property
If you use LockWindowUpdate, you don't need to set .Visible property
If speed is important, you don't want to use Me.TextWidth or Iif.
GetFocus() - this was needed in VB1, now listboxes have hWnd property
If you use LockWindowUpdate, you don't need to set .Visible property
As ameba pointed, not use textwidth. If you need to call
SendMessage ListHwnd, LB_SETHORIZONTALEXTENT, TextSize, 0&
use the loop on a similar fashion:
Dim ret As Long, sItmText As String
Dim i As Long, lResult As Long, lItmMax As Long
With List1
For i = 0 To .ListCount - 1
lResult = SendMessage(.hwnd, LB_GETTEXTLEN, i, ByVal 0)
If (lResult > lItmMax) Then
sItmText = .List(i)
lItmMax = lResult
End If
Next i
ret = SendMessage(.hwnd, LB_SETHORIZONTALEXTENT, lResult, ByVal 0)
End With
even adding it to your existing loop.
But take in mind all other comments regardind redesing.
SendMessage ListHwnd, LB_SETHORIZONTALEXTENT, TextSize, 0&
use the loop on a similar fashion:
Dim ret As Long, sItmText As String
Dim i As Long, lResult As Long, lItmMax As Long
With List1
For i = 0 To .ListCount - 1
lResult = SendMessage(.hwnd, LB_GETTEXTLEN, i, ByVal 0)
If (lResult > lItmMax) Then
sItmText = .List(i)
lItmMax = lResult
End If
Next i
ret = SendMessage(.hwnd, LB_SETHORIZONTALEXTENT, lResult, ByVal 0)
End With
even adding it to your existing loop.
But take in mind all other comments regardind redesing.
1st if ur source is a text file try using the Memory Mapping Method which will accelerate the reading process from the file.
2nd there's no way to load these items in a listbox so fast.
beside the list box won't be able to support that number of items unless you wanna design ur own one
y don't u try integrating HTML under your VB form.
i mean simply use the Microsoft Internet Controls OCX in your application, the load your file as HTML and make a link for each item. of course it's ur code that must create the link and obviously u must use some scripts like java or vb script... it 's not an easy way but it works if u r insisting on having 66000 items
2nd there's no way to load these items in a listbox so fast.
beside the list box won't be able to support that number of items unless you wanna design ur own one
y don't u try integrating HTML under your VB form.
i mean simply use the Microsoft Internet Controls OCX in your application, the load your file as HTML and make a link for each item. of course it's ur code that must create the link and obviously u must use some scripts like java or vb script... it 's not an easy way but it works if u r insisting on having 66000 items
EDDYKT
please hide the listbox before add items to it.
show it again when the adding ends.
doing that it will take from 39 to 8 seconds to add 30000 things to a listbox
but i still think in rspahitz comment "image the poor use who has to scroll through 66,000 items to find the desired one."
hope this helps
please hide the listbox before add items to it.
show it again when the adding ends.
doing that it will take from 39 to 8 seconds to add 30000 things to a listbox
but i still think in rspahitz comment "image the poor use who has to scroll through 66,000 items to find the desired one."
hope this helps
i personally think that additem is faster.
but ask Azrasound about it.
but ask Azrasound about it.
ASKER
OK, Let me explain more,
I've an OPC application that print out the system messages (error message) into the output log file. When the new log file exceed 1M size, I will delete the old one and rename the new to old log file.
I've another VB program that read both old and new ASCII file and display on the listbox. Each message will be timestamp and the user should be easy to go through the list box to check the progress of the program.
To AzraSound:
I did try to use database but the write performance from OPC application is poor. However the read is fast if I use database grid. The OPC application should be fast enough to write all log to log file without any delay.
To roverm:
I'm not sure what do you meant here. If I can use timer, the text in listbox must flicking when I add the new text.
To ameba, Richie_Simonetti:
If I have 66000 lines, it will take a lot amoiunt of time to go through each textline and find out the longest line width.
I can take out thr textwidth and try.
To olx:
I did set the listbox to invisible.
I've an OPC application that print out the system messages (error message) into the output log file. When the new log file exceed 1M size, I will delete the old one and rename the new to old log file.
I've another VB program that read both old and new ASCII file and display on the listbox. Each message will be timestamp and the user should be easy to go through the list box to check the progress of the program.
To AzraSound:
I did try to use database but the write performance from OPC application is poor. However the read is fast if I use database grid. The OPC application should be fast enough to write all log to log file without any delay.
To roverm:
I'm not sure what do you meant here. If I can use timer, the text in listbox must flicking when I add the new text.
To ameba, Richie_Simonetti:
If I have 66000 lines, it will take a lot amoiunt of time to go through each textline and find out the longest line width.
I can take out thr textwidth and try.
To olx:
I did set the listbox to invisible.
But you could get the actual length of every line when you are populating the list in first place!
Who needs precise width of the line - make it some big fixed value, or use: len() * textwidth("W")
Did you know that "On Error Resume Next" hides all errors?
Are you sure your listbox can hold more than 32768 items?
Did you know that "On Error Resume Next" hides all errors?
Are you sure your listbox can hold more than 32768 items?
Well, i have to put some ugly stuff to try the code :
Option Explicit
Private Type SIZE
cx As Long
cy As Long
End Type
Private Const LB_ADDSTRING = &H180
Private Const LB_SETHORIZONTALEXTENT = &H194
Private Const LB_GETTEXTLEN = &H18A
Private Const WM_GETFONT = &H31
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
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As SIZE) As Long
Public Sub AddToListBox(messages As String)
Dim Mess() As String, I As Long, Counter As Long
Dim lItm As Long
Dim lResult As Long, lItemmax As Long, sString As String
Dim uSize As SIZE
Dim lstDC As Long, lFont As Long, lFontOld As Long
On Error Resume Next
With lstMessages
.Clear
.Visible = False
Mess = Split(messages, vbCrLf) ' contains all lines with vbcrlf delimiter
Counter = UBound(Mess)
For I = 0 To Counter - 1
lItm = SendMessage(.hwnd, LB_ADDSTRING, 0, ByVal Mess(I))
lResult = SendMessage(.hwnd, LB_GETTEXTLEN, _
lItm, ByVal 0)
If (lResult > lItemmax) Then
sString = Mess(I)
lItemmax = lResult
End If
Next
lstDC = GetDC(.hwnd)
lFont = SendMessage(.hwnd, WM_GETFONT, 0, ByVal 0)
'Select the font in to the device context, and retain prior font
lFontOld = SelectObject(hdc, lFont)
lResult = GetTextExtentPoint32(hdc, sString, lResult, uSize)
SelectObject hdc, lFontOld
ReleaseDC .hwnd, hdc
SendMessage .hwnd, LB_SETHORIZONTALEXTENT, uSize.cx, 0&
.Visible = True
End With
End Sub
Private Sub Form_Load()
AddToListBox "n,mdnf,nds,fnsfn,dsfn,dsn ,ds" & vbCrLf & _
"n,ttftftftfdsn,ds" & vbCrLf & _
"n,mdnf,nds,fnsfn,dsfn,dsn ,ds" & vbCrLf & _
"n,mdnf,ncfdedsds,fnsfn,ds fn,dsn,ds" & vbCrLf & _
"n,mdnf,1234543nds,fnsfn,d sfn,dsn,ds " & vbCrLf & _
"n,mdnf,cdxdsnds,fnsfn,dsf n,dsn,ds" & vbCrLf & _
"n,mdnf,nds,fnsfn,dsfn,dsn ,ds" & vbCrLf & _
"n,mdnf,nds,knmmmmmmmmmmmm jhygtffnsf n,dsfn,dsn ,ds" & vbCrLf
End Sub
Option Explicit
Private Type SIZE
cx As Long
cy As Long
End Type
Private Const LB_ADDSTRING = &H180
Private Const LB_SETHORIZONTALEXTENT = &H194
Private Const LB_GETTEXTLEN = &H18A
Private Const WM_GETFONT = &H31
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
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As SIZE) As Long
Public Sub AddToListBox(messages As String)
Dim Mess() As String, I As Long, Counter As Long
Dim lItm As Long
Dim lResult As Long, lItemmax As Long, sString As String
Dim uSize As SIZE
Dim lstDC As Long, lFont As Long, lFontOld As Long
On Error Resume Next
With lstMessages
.Clear
.Visible = False
Mess = Split(messages, vbCrLf) ' contains all lines with vbcrlf delimiter
Counter = UBound(Mess)
For I = 0 To Counter - 1
lItm = SendMessage(.hwnd, LB_ADDSTRING, 0, ByVal Mess(I))
lResult = SendMessage(.hwnd, LB_GETTEXTLEN, _
lItm, ByVal 0)
If (lResult > lItemmax) Then
sString = Mess(I)
lItemmax = lResult
End If
Next
lstDC = GetDC(.hwnd)
lFont = SendMessage(.hwnd, WM_GETFONT, 0, ByVal 0)
'Select the font in to the device context, and retain prior font
lFontOld = SelectObject(hdc, lFont)
lResult = GetTextExtentPoint32(hdc, sString, lResult, uSize)
SelectObject hdc, lFontOld
ReleaseDC .hwnd, hdc
SendMessage .hwnd, LB_SETHORIZONTALEXTENT, uSize.cx, 0&
.Visible = True
End With
End Sub
Private Sub Form_Load()
AddToListBox "n,mdnf,nds,fnsfn,dsfn,dsn
"n,ttftftftfdsn,ds" & vbCrLf & _
"n,mdnf,nds,fnsfn,dsfn,dsn
"n,mdnf,ncfdedsds,fnsfn,ds
"n,mdnf,1234543nds,fnsfn,d
"n,mdnf,cdxdsnds,fnsfn,dsf
"n,mdnf,nds,fnsfn,dsfn,dsn
"n,mdnf,nds,knmmmmmmmmmmmm
End Sub
>>I did try to use database but the write performance from OPC application is poor.
Did you ever consider writing to the DB instead of a log file? An insert into the DB is quick, you would have more ability to query the log, maintain a longer history of messages logged by the OPC server, allow for simple searches, etc.
Did you ever consider writing to the DB instead of a log file? An insert into the DB is quick, you would have more ability to query the log, maintain a longer history of messages logged by the OPC server, allow for simple searches, etc.
ASKER
OK, Let me explain more,
I've an OPC application that print out the system messages (error message) into the output log file. When the new log file exceed 1M size, I will delete the old one and rename the new to old log file.
I've another VB program that read both old and new ASCII file and display on the listbox. Each message will be timestamp and the user should be easy to go through the list box to check the progress of the program.
To AzraSound:
I did try to use database but the write performance from OPC application is poor. However the read is fast if I use database grid. The OPC application should be fast enough to write all log to log file without any delay.
To roverm:
I'm not sure what do you meant here. If I can use timer, the text in listbox must flicking when I add the new text.
To ameba, Richie_Simonetti:
If I have 66000 lines, it will take a lot amoiunt of time to go through each textline and find out the longest line width.
I can take out thr textwidth and try.
To olx:
I did set the listbox to invisible.
I've an OPC application that print out the system messages (error message) into the output log file. When the new log file exceed 1M size, I will delete the old one and rename the new to old log file.
I've another VB program that read both old and new ASCII file and display on the listbox. Each message will be timestamp and the user should be easy to go through the list box to check the progress of the program.
To AzraSound:
I did try to use database but the write performance from OPC application is poor. However the read is fast if I use database grid. The OPC application should be fast enough to write all log to log file without any delay.
To roverm:
I'm not sure what do you meant here. If I can use timer, the text in listbox must flicking when I add the new text.
To ameba, Richie_Simonetti:
If I have 66000 lines, it will take a lot amoiunt of time to go through each textline and find out the longest line width.
I can take out thr textwidth and try.
To olx:
I did set the listbox to invisible.
ASKER
Don't know what happen to last message?->
To ameba:
>>Who needs precise width of the line - make it some big fixed value, or use: len() * textwidth
I will get back to this and try out.
Yes it work over the magic number. I am really sure because I put the line number in front of each line.
To AzraSound:
It treat each log message is separated so that each update message will require open database, open connection, write data and close database. What do you think the speed is.
To Richie_Simonetti:
I'wll try this and let you know.
To ameba:
>>Who needs precise width of the line - make it some big fixed value, or use: len() * textwidth
I will get back to this and try out
To ameba:
>>Who needs precise width of the line - make it some big fixed value, or use: len() * textwidth
I will get back to this and try out.
Yes it work over the magic number. I am really sure because I put the line number in front of each line.
To AzraSound:
It treat each log message is separated so that each update message will require open database, open connection, write data and close database. What do you think the speed is.
To Richie_Simonetti:
I'wll try this and let you know.
To ameba:
>>Who needs precise width of the line - make it some big fixed value, or use: len() * textwidth
I will get back to this and try out
ASKER
By the way, I use windows 2000, vb6 sp5
ASKER
I try all the method and event comment out the textwidth line. The result is almost the same; just 1 sec faster.
I believe it is not textwidth to slow it down. It is additem.
Any suggestion
Should I try using datasource property?
I believe it is not textwidth to slow it down. It is additem.
Any suggestion
Should I try using datasource property?
>>It treat each log message is separated so that each update message will require open database, open connection, write data and close database. What do you think the speed is.
It won't be so slow that you will notice a huge performance loss. Then, you have the added bonus of quickly searching this database based on the user's preferences. For example, instead of just coming up and showing every entry in the log, they can enter in maybe a start and end date, messages that contain a certain message, critical messages only, etc. Plus, then you have an easy means of allowing them to delete all log entries, delete selected entries, etc (all the benefits of working with data in a database).
It won't be so slow that you will notice a huge performance loss. Then, you have the added bonus of quickly searching this database based on the user's preferences. For example, instead of just coming up and showing every entry in the log, they can enter in maybe a start and end date, messages that contain a certain message, critical messages only, etc. Plus, then you have an easy means of allowing them to delete all log entries, delete selected entries, etc (all the benefits of working with data in a database).
ASKER
AzraSound,
I think of that already. The main reason we drop the database because we may need to write 20 or more messsages in a second into the log file (if required). Using database is not a proper method to do. Do you think so?
The other problem is we have to maintain how many line in the database so that I have to delete the old one before I added the new one in. To doing so the database file size will keep growing as well and I have to compact the database once a while. I don't think we have time to do that in OPC server.
Those all kinds of problems will lead us to drop database method.
I think of that already. The main reason we drop the database because we may need to write 20 or more messsages in a second into the log file (if required). Using database is not a proper method to do. Do you think so?
The other problem is we have to maintain how many line in the database so that I have to delete the old one before I added the new one in. To doing so the database file size will keep growing as well and I have to compact the database once a while. I don't think we have time to do that in OPC server.
Those all kinds of problems will lead us to drop database method.
ASKER
rspahitz :
I tried using multi-text box. I add set my whole text string (text wiht vbcrlf delimiter) into the textbox in one short but the alignment doesn't look really good on text box
8-<
I tried using multi-text box. I add set my whole text string (text wiht vbcrlf delimiter) into the textbox in one short but the alignment doesn't look really good on text box
8-<
Database speed may be quicker than you think, and if you are hitting the database that often, you could probably just leave a connection open always (and remove the performance issue of opening/closing that connection on every call). I imagine you would use a full rich database engine like SQL Server.
>>The other problem is we have to maintain how many line in the database so that I have to delete the old one before I added the new one in.
Thats where you could use the rich features of SQL Server. You can schedule it to run a script every so often (determined by you) that would go and remove records as needed. This would not require your OPC server to do anything regarding that since it would be running in a separate process (SQL Server's) and executed on a schedule.
>>The other problem is we have to maintain how many line in the database so that I have to delete the old one before I added the new one in.
Thats where you could use the rich features of SQL Server. You can schedule it to run a script every so often (determined by you) that would go and remove records as needed. This would not require your OPC server to do anything regarding that since it would be running in a separate process (SQL Server's) and executed on a schedule.
ASKER
AzraSound,
For those small job, I should mention that I can only use MS access database. SQL server may be a little bit over kill. 8->
For those small job, I should mention that I can only use MS access database. SQL server may be a little bit over kill. 8->
Oh, I see...
Are you writing an OPC client or OPC server?
Are you writing an OPC client or OPC server?
ASKER
OPC Server
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Wow, Listview is lot lot fastest than listbox.
That's great. Thanks ameba
Do you know what is the reason?
That's great. Thanks ameba
Do you know what is the reason?
ASKER
Good thinking
Thanks.
>Do you know what is the reason?
Maybe Sorted property of the listbox was set to True?
>Do you know what is the reason?
Maybe Sorted property of the listbox was set to True?
' here is how to set width of the column to tmpwidth (PIXELS)
Private Const LVM_SETCOLUMNWIDTH = (LVM_FIRST + 30)
ret = SendMessage(LV.hwnd, LVM_SETCOLUMNWIDTH, 0, ByVal tmpwidth)
' there is also another msg to set column width automatically...
Private Const LVM_SETCOLUMNWIDTH = (LVM_FIRST + 30)
ret = SendMessage(LV.hwnd, LVM_SETCOLUMNWIDTH, 0, ByVal tmpwidth)
' there is also another msg to set column width automatically...
>set column width automatically...
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
Private Const LVM_SETCOLUMNWIDTH = &H1000 + 30
Private Const LVSCW_AUTOSIZE = -1
Private Const LVSCW_AUTOSIZE_USEHEADER = -2
Private Sub Form_Click()
AutoSizeColumns Me.Listview1
End Sub
Private Sub AutoSizeColumns(Listview As Listview, Optional ByVal UseHeader As Boolean = False)
Dim i As Integer, lParam As Long
If UseHeader = False Then
lParam = LVSCW_AUTOSIZE
Else
lParam = LVSCW_AUTOSIZE_USEHEADER
End If
For i = 0 To Listview.ColumnHeaders.Cou nt - 1
SendMessage Listview.hwnd, LVM_SETCOLUMNWIDTH, i, ByVal lParam
Next
End Sub
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
Private Const LVM_SETCOLUMNWIDTH = &H1000 + 30
Private Const LVSCW_AUTOSIZE = -1
Private Const LVSCW_AUTOSIZE_USEHEADER = -2
Private Sub Form_Click()
AutoSizeColumns Me.Listview1
End Sub
Private Sub AutoSizeColumns(Listview As Listview, Optional ByVal UseHeader As Boolean = False)
Dim i As Integer, lParam As Long
If UseHeader = False Then
lParam = LVSCW_AUTOSIZE
Else
lParam = LVSCW_AUTOSIZE_USEHEADER
End If
For i = 0 To Listview.ColumnHeaders.Cou
SendMessage Listview.hwnd, LVM_SETCOLUMNWIDTH, i, ByVal lParam
Next
End Sub
ASKER
>>Maybe Sorted property of the listbox was set to True?
No , it is false 8->
Anyway thanks
No , it is false 8->
Anyway thanks
if the control is not visible during the operations, it will go much smoother.