Solved

Alternative listview

Posted on 2002-03-14
36
950 Views
Last Modified: 2013-12-25
Hi,
I am using the listview from Windows Common Controls 6.0 (SP4) The problem is that when I use.

ListView1.ListItems(i).Icon = X

the scroll position of the listview resets to the beginning. I need an update or totally new control (freeware) which is compatible with the ImageList control but wont have the same problem I am currently experiencing.

Thanks
0
Comment
Question by:adam8
  • 19
  • 14
  • 2
  • +1
36 Comments
 
LVL 4

Expert Comment

by:PBuck
ID: 6864823
This should interest you very much then!

http://www.vbaccelerator.com/ and navigate to CONTROLS on the left-hand side and try the SGRID.  Looks like the Outlook grid initially, but after you download it and try out the sample - it may be exactly what you need.

Give it a try.  Hope this helps!
0
 
LVL 18

Expert Comment

by:mdougan
ID: 6865457
What the listview is doing is scolling to ensure that the currently selected listitem is in view.  So, if it's scrolling to the top, it's because no item in the list has been selected.  If you can find a way to select one of the currently visible listitems before you issue that code, then the control wont scroll on you.

Which mode are you using, Icon, Small Icon, List, Report?  
0
 
LVL 1

Author Comment

by:adam8
ID: 6867366
I am using Icon view and I tried that and it works but the prob is what if the user already has a selection out of the visible range. I don't want to interfere with any other selections yet I don't want the position to reset.

As for you PBuck I will download that control to see how it is.
0
 
LVL 18

Expert Comment

by:mdougan
ID: 6868213
I tried using LockWindowUpdate and SendMessage to stop window refreshing, but neither of those worked either.
0
 
LVL 1

Author Comment

by:adam8
ID: 6869553
well if you come up with something
0
 
LVL 39

Expert Comment

by:abel
ID: 6869566
You may consider a way that's apparently used by (i.e.) the Windows Explorer: update the icons as soon as they become visible, and postpone it as long as they are hidden from view. This requires using either a timer to test for appearing items, or subclassing the scroll-event or the likes of the listview. If you like using an approach like this I may be able to help you further, but it will require quite some extra coding to get it neatly done.
0
 
LVL 1

Author Comment

by:adam8
ID: 6869740
okay, maybe I could try something like what you suggested.
0
 
LVL 1

Author Comment

by:adam8
ID: 6869841
Hi,
Actually, now that I read your question I realised I have already done that and it is on a timer.
I have no trouble with getting the visible items and loading icons for them but when I set the new icon for a visible item
the scroll bar goes to the start if no selection is made.

If you could give me a way to add an event for the scrolling of a listview that would be great.

So if the user scrolled down or up on the listview I could make it check for visible items
rather than the timer checking at a regular interval.
0
 
LVL 39

Expert Comment

by:abel
ID: 6872681
> If you could give me a way to add an event for the scrolling of a listview that would be great.
Well, just adding an event... What you can do is subclass the window holding the ListView20WndClass (vb6 listview) for the WM_VSCROLL message (or WM_HSCROLL for that matter) and make sure it isn't processed by CallWindowProc.

If you need an example on how to subclass a window, go here:
http://msdn.microsoft.com/library/en-us/vbcon98/html/vbconpassingfunctionpointerstodllprocedurestypelibraries.asp
0
 
LVL 1

Author Comment

by:adam8
ID: 6873503
could you please just tell me what I need to do because I can't be bothered reading all the crap on the link.
0
 
LVL 39

Expert Comment

by:abel
ID: 6874136
> because I can't be bothered reading all the crap on the link
It's really very usefull and only a 5 min. read. But I'll do the copy and pasting and adjusting for you, just because it's such a rainy day over here ;)

'Code from http://msdn.microsoft.com/library/en-us/vbcon98/html/vbconpassingfunctionpointerstodllprocedurestypelibraries.asp
'Adjusted for use with ListView from VB6

'Place a checkbox on the form (name: chkSubclass) and add this to the form code:
Private Sub chkSubclass_Click()
    If chkSubclass.Value = vbChecked Then
        gHW = ListView1.hwnd    'Your listview here
        Hook
    Else
        Unhook
    End If
End Sub



'Place the following code in a module
Public Const WM_HSCROLL = &H114
Public Const WM_VSCROLL = &H115
Public Const GWL_WNDPROC = -4


Public Sub Hook()
   lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)
End Sub

Public Sub Unhook()
   SetWindowLong gHW, GWL_WNDPROC, lpPrevWndProc
End Sub

Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    'Just add other messages if they bug you
    If uMsg = WM_VSCROLL Or uMsg = WM_HSCROLL Then
        'Stop processing, no scrolling allowed
        WindowProc = DefWindowProc(hw, uMsg, wParam, lParam)
    Else
        'Call prev. window procedure, the original!
        WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)
    End If
End Function


Hope this helps,

Cheers
Abel
0
 
LVL 1

Author Comment

by:adam8
ID: 6883945
So how do I know when the user scrolls?
0
 
LVL 39

Expert Comment

by:abel
ID: 6886314
You don't have to know. Suppose your function to set the icon is called SetIcon(Index As Long), then you simply alter that function as follows:

Sub SetIcon(Index As Long)
    gHW = ListView1.hwnd    'Your listview here
    Hook
    '... your seticon code    
    UnHook
End Sub

In other words, only when you change the icon you prevent scrolling.

And if you do want to know when the user scrolls, just for the fun of it, see the WindowProc, it happens there. In the if-statement to be more precise.

Cheers,
Abel
   
0
 
LVL 1

Author Comment

by:adam8
ID: 6887159
but when the images are being processed nothing can be clicked anyway because it is in a loop, so therefore the user wouldn't be scrolling to much anyway :-)
0
 
LVL 39

Expert Comment

by:abel
ID: 6887446
That's good. Does it work? I couldn't test it with your code of course ;-)
0
 
LVL 1

Author Comment

by:adam8
ID: 6888179
it is in a loop, everything is freezed until the loop completes so your code wouldn't really do much.
0
 
LVL 1

Author Comment

by:adam8
ID: 6893633
wait a minute. WIll your code stop the program from scrolling to the start of the list every time?
0
 
LVL 1

Author Comment

by:adam8
ID: 6893654
I tried the hook thing and I put the hook exactly where it changes the icon and put the unhook exactly after that but it freezes the program and i have to end task.
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 1

Author Comment

by:adam8
ID: 6893657
any other suggestions
0
 
LVL 39

Expert Comment

by:abel
ID: 6894468
May I have a look at your code? I mean the part of the loop where you change the pictures (you know, the part that makes the listbox scroll unintentionally) plus the way you implemented the hooking code. I'll try to modify it so that it won't scroll the listbox when you change the pictures.
0
 
LVL 1

Author Comment

by:adam8
ID: 6895819
hi, here is the whole of the timer code and the problem is occured at

flb.ListItems(i).Icon = Icons(i)

I put the Hook code before that and Unhook after that. I tried putting the hook code in a command button just to check and Visual Basic closed.


    On Error Resume Next
    Dim i As Long
    Dim lstVisCount As Integer
    Dim FirstVis As Boolean
    Dim SelItem As Long
   
    If tabMain.Tab = 1 Then Exit Sub
   
    If flb.ListItems.Count = 0 Then
        tmrLoadThumbnails.Enabled = False
        Exit Sub
    End If
   
    If FirstVisible = flb.GetFirstVisible.Index Then
        Exit Sub
    End If
   
    lstVisCount = flb.GetFirstVisible.Index + GetVisibleCount(flb) - 1
   
    SelItem = flb.SelectedItem.Index
    FirstVisible = flb.GetFirstVisible.Index
   
    'FirstVis = flb.ListItems(FirstVisible).Selected
    'flb.ListItems(FirstVisible).Selected = True
   
    If FirstVis = False Then
        flb.ListItems(FirstVisible).Selected = False
    End If
   
    gHW = flb.hwnd
   
    'flb.ListItems(SelItem).Selected = True
   
StartLoop:
   
    'from first visible item to the next visible items
    For i = FirstVisible To lstVisCount
        If Icons(i) > 0 Then GoTo LoadNextThumbnail
       
        picSrc.Picture = LoadPicture(flbList(i))
   
       
        hWidth = picSrc.Width
        hHeight = picSrc.Height
   
        If hHeight > 76.8 Then
            hWidth = 76.8 * picSrc.Width / picSrc.Height
            hHeight = 76.8
        End If
   
        If hWidth > 102.4 Then
            hHeight = 102.4 * picSrc.Height / picSrc.Width
            hWidth = 102.4
        End If
   
        picThumb.PaintPicture picSrc, (picThumb.Width - hWidth) / 2, (picThumb.Height - hHeight) / 2, hWidth, hHeight
        imgList.ListImages.Add , , picThumb.Image
        Icons(i) = imgList.ListImages.Count
       
        flb.ListItems(i).Icon = Icons(i)
       
        picThumb.Cls
       
        flb.Refresh
LoadNextThumbnail:
    Next
0
 
LVL 1

Author Comment

by:adam8
ID: 6895822
It may seema bit confusing but the code that is making it scroll is this

flb.ListItems(i).Icon = Icons(i)
0
 
LVL 1

Author Comment

by:adam8
ID: 6909530
this is not going anywhere
0
 
LVL 39

Expert Comment

by:abel
ID: 6910883
I am terribly sorry, but I did not receive any email notification about your new comments. Just because you still existed on my todo-list made me look back to this q. to see if anything was happening.

I'll copy and paste your code into a testproject and see if I can make it work.
0
 
LVL 1

Author Comment

by:adam8
ID: 6912285
thanks
0
 
LVL 39

Expert Comment

by:abel
ID: 6912480
I have tried the principle goal of your code: updating a listview with new icons when an item is selected. I noticed, that even without hooking, the scroll does not appear! Unless you set Sorted to true and you also change the contents of the listview. This means that what you are experiencing is something either unique to your computer/system or version of Common Controls, or you should set Sorted to False and everything is fine.

If this is indeed a bug then I will re-investigate for you, but now in another direction. I was aiming at "normal", yet undesirable behaviour, but now it appears to be not so normal anymore.

Abel
0
 
LVL 39

Expert Comment

by:abel
ID: 6912488
I am sorry, I posted without realising some details about this thread. I reread it and:

you said:
> I am using Icon view
And I was using report view. Forget that last comment, I'll test the full solution and give you a new post when it works without crashing.

Cheers

0
 
LVL 39

Expert Comment

by:abel
ID: 6912507
I do have a working version now. Unfortunately I have to hurry to an appointment, but I'll update later today (GMT+1). It currently includes hooking (no crash at all) and setting the Listview to not visible and then visible again. The latter may not be desirable, but it's needed to paint the icons. It doesn't flicker with me, but if it does flicker with you, we'll try something else.
0
 
LVL 1

Author Comment

by:adam8
ID: 6912671
I will be happy to give it a try. How did you know that making the listview invisible when hooking would work?
0
 
LVL 1

Author Comment

by:adam8
ID: 6912675
I forgot to ask, that won't affect the current code I have will it, I know I will need to add in the extra code but it won't change the way that I load the icons and all that will it?
0
 
LVL 39

Accepted Solution

by:
abel earned 300 total points
ID: 6913957
Let's say you put your current code in a sub called SetLVIcons(). This is the code you have to paste to get it all working:

    Dim i As Long
   
    HookListview flb
    SetLVIcons                  'Your sub
    UnhookListview
    flb.Visible = False
    flb.Visible = True


And place this in your module:

Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function DefWindowProc Lib "user32" Alias "DefWindowProcA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Const WM_STYLECHANGED = &H7D
Public Const GWL_WNDPROC = (-4)


Public gPrevWndProc As Long
Public gHW As Long
Public gLV As ListView

Public Sub HookListview(lv As ListView)
    If Not lv Is Nothing Then
        If gHW <> 0 Then
            UnhookListview
        End If
        Set gLV = lv
        gHW = gLV.hwnd
        gPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)
    End If
End Sub

Public Sub UnhookListview()
    If gHW <> 0 And gPrevWndProc <> 0 Then
        SetWindowLong gHW, GWL_WNDPROC, gPrevWndProc
        gHW = 0
        Set gLV = Nothing
    End If
End Sub

Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    If uMsg = WM_STYLECHANGED Then
        WindowProc = DefWindowProc(hw, uMsg, wParam, lParam)
    Else
        'Call prev. window procedure, the original!
        WindowProc = CallWindowProc(gPrevWndProc, hw, uMsg, wParam, lParam)
    End If
End Function
0
 
LVL 39

Expert Comment

by:abel
ID: 6913981
> How did you know that making the listview invisible when hooking would work?
Hooking worked already. The reason that it did not work for you is either an error in the WindowProc procedure, which causes VB to crash, or an error in the Hook or Unhook function, which may also lead to unpredictable behaviour. When you subclass, debugging can be very hard because VB can't determine what's happening (and that's precisely what we're after!) and may crash on any error while you hook.

This also means that using "On Error Resume Next" in your code may help VB from crashing, but it may also hide certain errors that disturb other procedures and in turn, they err and crash VB.

If you start using this code and VB crashes again, try to compile your code into an executable. This has two benefits. Firstly, it shows what errors there are from mistyping (I hope you use Option Explicit?!?!). Secondly, when it crashes as an compiled exe you may have the opportunity to see a messagebox hinting you about the error.
0
 
LVL 39

Expert Comment

by:abel
ID: 6913998
Now about the code.

The method showed here is basically similar to the original method, except for a few changes. First of all, I made the functions/subs that enable/disable the subclassing a bit more safe to use, hoping it will avoid misuse.
Then you may notice a different message being blocked: WM_STYLE_CHANGED. As it turned out, not WM_VSCROLL/WM_HSCROLL were causing the trouble, but WM_STYLE_CHANGED. Why it scrolls the listview I don't know, but it appears as if it acts so to force a redraw of the listview.
Now we have to do that manually, hence the third change: visibility to false and to true. You may want to use SendMessage with WM_PAINT and a drawing area instead (hope it works), but this is by far the easiest method to implement and flawless, except for a small flickering (although I didn't see any).

Hopefully it works as well for you as it did for me ;-)

Cheers!
Abel
0
 
LVL 1

Author Comment

by:adam8
ID: 6914286
I am going away for a 2 days but I will give it a go when I get back.

THanks for your hard work.
0
 
LVL 1

Author Comment

by:adam8
ID: 6922096
that is incredible. It works. I have been having this problem for months and no one could help.

Thank you so much. I don't think I can give more than 300 points or else I would give you 500.

It seems good and doesn't flicker. I have it in my code like this

        HookListview flb
        flb.ListItems(i).Icon = Icons(i)
        UnhookListview
        flb.Visible = False
        flb.Visible = True

I am not sure if that is what you intended but it works fine and the
        flb.Visible = False
        flb.Visible = True
doesn't seem to do anything so I am not sure if I need it or not.

0
 
LVL 39

Expert Comment

by:abel
ID: 6922783
> I am not sure if that is what you intended
Yes it is exactly how I intended it.

> doesn't seem to do anything so I am not sure if I need it or not.
It is too quick for the eye to see, but if you'd use Spy++ to look at the messages sending hence and forth, you'll see that a lot *is* happening. On my system, when I removed the invisible/visible part, the icons disappeared and were not drawn again, at least not before I manually used the scrollbar or did something else (like hovering another window over it) to force the control to redraw itself. But in case you are wondering, remove those two lines and you'll see for yourself. Maybe it was only needed on my system because I happen to have some obscure video card.

> Thank you so much. I don't think I can give more than 300 points or else I would give you 500.
You're welcome, I'm glad it worked ;-)
Don't worry about the points. I'm just trying to help people around here if I get the chance. "Just" a thanks is more appreciated than huge amounts of points. And indeed, 300 pts is the maximum out here, or you have to create another (blank) question.

Cheers!
Abel
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now