Hi Paul,
I've had GetLastError() = 6, AfxGetWinErrMsg: "The handle is invalid." somewhere in my code that wasn't causing my error trapping to fire. (On Error GoTo...)
It turns out to be in the Event frmMain_Load.
'' GetLastError() = 0
'' This code functions correctly, populating the ListView header
With frmMain.ListView1 '' <-- This does not throw an error
? .Items.Count '' <-- This does not throw Windows error 6, with the following lines are commented out. Prints 0
'' Any if these cause "The handle is invalid." error
.Items.Clear
.Columns.Add( "Time", 50, TextAlignment.Center )
.Columns.Add( "Entry", 720, TextAlignment.Left )
.Columns.Add( "ID", 45, TextAlignment.Center )
.Columns.Add( "Event", 90, TextAlignment.Center )
End With
'' GetLastError() = 6
With/End With makes no difference.
Thanks Clive I am able to reproduce this. Seems to be somewhat related to the win api call I make in the Clear method to set the number of items of the listview (SetItemCountEx). Seems to be, but I'm not 100% sure yet. I haven't had much programming time since Christmas has started.
Hi Paul, something for after the holidays:
My app started crashing at:
frmMain.ListView1.Items.Clear
I moved this line to the line immediately after frmMain.ListView1.BeginUpdate and no more crashing and the ListView refreshed very fast.
Off-Topic: StatusBar appears as expected the first time a form (popup form in this case) is shown, but the StatusBar does not appear on subsiquent show/showdialog. App restart is required.
Hi Paul,
wfxListView.inc:
To solve a very slow refresh:
I tried commenting out AfxRedrawWindow(this.hWindow) at Lines 661 and 678 which was being called each time a fore or back colour was changed. This added approx 175ms to the refresh each time per change. So a ListView with 10 colour changes had almost 2 seconds added to when the system gave back control. (Slow enough that the Rows could be counted as they reappeared from the bottom to the top Row.)
As Refresh is required to display colour changes, after commenting out the above, I added the following to my code at the end of the colour change routine:
If IsWindowVisible( ListView.hWindow ) Then '' Probably not required.
ListView.Refresh
End If
Colour changes and a ListView.Refresh is now visually instantaneous on my machine.
This was just an experiment, but it worked.
Quote from: SeaVipe on January 07, 2020, 05:15:41 PM
Hi Paul,
wfxListView.inc:
To solve a very slow refresh:
I tried commenting out AfxRedrawWindow(this.hWindow) at Lines 661 and 678 which was being called each time a fore or back colour was changed. This added approx 175ms to the refresh each time per change. So a ListView with 10 colour changes had almost 2 seconds added to when the system gave back control. (Slow enough that the Rows could be counted as they reappeared from the bottom to the top Row.)
As Refresh is required to display colour changes, after commenting out the above, I added the following to my code at the end of the colour change routine:
If IsWindowVisible( ListView.hWindow ) Then '' Probably not required.
ListView.Refresh
End If
Colour changes and a ListView.Refresh is now visually instantaneous on my machine.
This was just an experiment, but it worked.
Hi Clive, when I uploaded version 2.0.2 on December 16th, I added code to those functions to help reduce the refresh problem. Does your code look like the following? If not, then maybe you don't have the latest code?
property wfxListViewSubItem.BackColor() as COLORREF
property = _BackColor
end property
property wfxListViewSubItem.BackColor( byval nValue as COLORREF )
_BackColor = nValue
if this.hWindow THEN
if ListView_IsItemVisible(this.hWindow, this.ItemIndex) then
AfxRedrawWindow(this.hWindow)
end if
end if
end property
property wfxListViewSubItem.ForeColor() as COLORREF
property = _ForeColor
end property
property wfxListViewSubItem.ForeColor( byval nValue as COLORREF )
_ForeColor = nValue
if this.hWindow THEN
if ListView_IsItemVisible(this.hWindow, this.ItemIndex) then
AfxRedrawWindow(this.hWindow)
end if
end if
end property
Hi Paul,
Here is the code that I was referring to:
property wfxListViewSubItem.BackColor() as COLORREF
property = _BackColor
end property
property wfxListViewSubItem.BackColor( byval nValue as COLORREF )
_BackColor = nValue
'if this.hWindow THEN
'if ListView_IsItemVisible(this.hWindow, this.ItemIndex) then
'AfxRedrawWindow(this.hWindow)
'end if
'end if
end property
property wfxListViewSubItem.ForeColor() as COLORREF
property = _ForeColor
end property
property wfxListViewSubItem.ForeColor( byval nValue as COLORREF )
_ForeColor = nValue
'if this.hWindow THEN
' if ListView_IsItemVisible(this.hWindow, this.ItemIndex) then
'AfxRedrawWindow(this.hWindow)
' end if
'end if
end property
I am a little confused. You have the important lines commented out. If you uncomment them, does it speed up the coloring? Or did you comment them because you were testing for solution to slowness?
Commented out - Fast update, not commented out - very Slow update.
Thanks Clive, I understand the problem better now. To fix it, I gave every Item and SubItem access to the flag that indicates that BeginUpdate is active. If that flag is True then I do not issue a redraw. When EndUpdate is called, a redraw is done. My few tests seem to confirm that this new logic is working correctly.
Thanks, Paul!
Try the new files attached to this post to see how it reacts to the data in your project. Please let me know if it works and there is a speed up when BeginUpdate is activated.
Hi Paul, a bit slow to populate the ListView. Very slow to apply colour changes after the ListView is populated. 6 Col x 30 Rows.
I make a lot of colour changes depending on the view. The subject portion of my main app is the Journal. It's limited to the 6 x 30 for now. The ListView gets repopulated as often as the User make additions to the day. After each modification (1 Row at a time Ex. 07:30 | User entry text | ID1 | ID2 | Type | Category ) The colours reflect the Type/Category values per Row (Reminder, Complete etc.) Plus the overall ListView has 3 default colour themes based on Past, Present and Future dates. So lots of colour changes.
Interesting. For your function that applies the color changes to the rows, do you wrap that in a BeginUpdate and EndUpdate ?
I really need to figure out why such a slowdown would be seen. I will go over the code again today to see if I've missed something.
...and I assume that you have the OddRowColorEnabled property set to False.
For my test, I am using a 1000 row ListView with 4 columns. All cells get recolored (foreground and background) whenever I press a button. The average time to recolor is 0.015 seconds. That's pretty fast and I don't see any visual on-screen flicker.
Here is the recolor routine that I am using:
Sub set_rows_colour( byref ListView as wfxListView, ByVal bg as COLORREF, byval fg as COLORREF )
? "Recolor start: "
dim as double t1, t2
t1 = timer
With ListView
' Because we are manually setting specific foreground and
' background colors, we do not want any color defined for
' the OddRowColor property to interfere.
.OddRowColorEnabled = false
.BeginUpdate
For iRow as long = 0 to .Items.Count - 1
For iCol as long = 0 to .Item(iRow).SubItems.Count - 1
.Item(iRow).SubItem(iCol).ForeColor = fg
.Item(iRow).SubItem(iCol).BackColor = bg
next iCol
Next iRow
.EndUpdate
End With
t2 = timer
? "Recolor end: "; t2 - t1; " seconds."
End Sub
Hi Paul,
(Using the 2 files you posted yesterday)
This line compiles okay, but has no effect:
frmMainJ.lvJournal.OddRowColorEnabled = False
The more text in the ListView, the longer the refresh.
With all of the following code removed, the average refresh is about 60ms, added back in, the refresh ranges between 2500ms and 6500ms.
Here is that code:
Sub set_row_colour( ByRef ListView as wfxListView, ByVal fg as COLORREF, ByVal bg as COLORREF, ByVal ubRow as UByte, ByVal caller As String = "Unknown" )
With ListView
For iCol as Long = 0 to 5
.Item( ubRow ).SubItem( iCol ).ForeColor = fg '' <-- Comment out this line and the refresh time is about 70ms
Next iCol
End With
End Sub
''
''
Sub do_colour_event_types( ByRef ListView as wfxListView, ByVal this_type as Long, ByVal caller As String = "Unknown" )
'? "do_colour_event_types"; caller
For i as Integer = 1 to 30
If js( jdb.RecPtr ).Entry_Type( i ) = this_type OrElse js( jdb.RecPtr ).Entry_Category( i ) = this_type then
'' User select
set_row_colour( ListView, colors.Blue, 0 , i - 1 )
ElseIf js( jdb.RecPtr ).Entry_Type( i ) = jdb.Entry_Modifier( "Done" ) OrElse js( jdb.RecPtr ).Entry_Category( i ) = jdb.Entry_Modifier( "Done" ) then
set_row_colour( ListView, colors.Gray, 0 , i - 1 )
ElseIf js( jdb.RecPtr ).Entry_Type( i ) = jdb.Entry_Modifier( "Daily" ) OrElse js( jdb.RecPtr ).Entry_Category( i ) = jdb.Entry_Modifier( "Daily" ) then
set_row_colour( ListView, colors.DarkRed , 0 , i - 1 )
Else
set_row_colour( ListView, colors.SystemWindowText, 0 , i - 1 )
End If
Next i
'' Refresh is only required to display colour changes if
'' wfxListView.inc, AfxRedrawWindow(this.hWindow) is commented out.
'If IsWindowVisible( ListView.hWindow ) Then '' Required?
' ListView.Refresh
'End If
End Sub
I hope this is helpful.
Hi Clive, I am not at my development computer but looking at your code that you posted I don't see BeginUpdate / EndUpdate code as part of your routines. To speed up the recolor I expect that if you change your do_colour_event_types function to the following then it should be a lot faster:
Sub do_colour_event_types( ByRef ListView as wfxListView, ByVal this_type as Long, ByVal caller As String = "Unknown" )
'? "do_colour_event_types"; caller
ListView.BeginUpdate
For i as Integer = 1 to 30
If js( jdb.RecPtr ).Entry_Type( i ) = this_type OrElse js( jdb.RecPtr ).Entry_Category( i ) = this_type then
'' User select
set_row_colour( ListView, colors.Blue, 0 , i - 1 )
ElseIf js( jdb.RecPtr ).Entry_Type( i ) = jdb.Entry_Modifier( "Done" ) OrElse js( jdb.RecPtr ).Entry_Category( i ) = jdb.Entry_Modifier( "Done" ) then
set_row_colour( ListView, colors.Gray, 0 , i - 1 )
ElseIf js( jdb.RecPtr ).Entry_Type( i ) = jdb.Entry_Modifier( "Daily" ) OrElse js( jdb.RecPtr ).Entry_Category( i ) = jdb.Entry_Modifier( "Daily" ) then
set_row_colour( ListView, colors.DarkRed , 0 , i - 1 )
Else
set_row_colour( ListView, colors.SystemWindowText, 0 , i - 1 )
End If
Next i
ListView.EndUpdate
'' Refresh is only required to display colour changes if
'' wfxListView.inc, AfxRedrawWindow(this.hWindow) is commented out.
'If IsWindowVisible( ListView.hWindow ) Then '' Required?
' ListView.Refresh
'End If
End Sub
Hi Paul,
In my code, BeginUpdate and EndUpdate are throwing Error 6, There are 2 ListView controls on a form and regardless of whether or not there is any code between BeginUpdate and EndUpdate an error 6 is thrown for each ListView, but, it doesn't invoke the error handler. Print GetLastError() catches the error (and clears it).
With frmMainJ.lvEntryType
.BeginUpdate
.Items.Clear '' Leave here, faster update.
For i As Integer = LBound( accumulator ) To UBound( accumulator )
j = .Items.Add( Left( accumulator( i ), 10 ) )
Dim As String s = Mid( accumulator( i ), 12, 50 )
.Item( j ).SubItems.Add( s )
Print GetLastError() ' prints 6 once per loop
Next i
.EndUpdate
End with
Comment out BeginUpdate and EndUpdate results in no error but refresh is slow.
Thanks, Paul,
Perfect, BeginUpdate and EndUpdate worked in do_colour_event_types without error 6 and was very fast. However, BeginUpdate and EndUpdate are still throwing error 6 in the code that adds rows and cols.