Link to home
Start Free TrialLog in
Avatar of adam8
adam8

asked on

Alternative listview

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
Avatar of PBuck
PBuck

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!
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?  
Avatar of adam8

ASKER

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.
I tried using LockWindowUpdate and SendMessage to stop window refreshing, but neither of those worked either.
Avatar of adam8

ASKER

well if you come up with something
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.
Avatar of adam8

ASKER

okay, maybe I could try something like what you suggested.
Avatar of adam8

ASKER

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.
> 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
Avatar of adam8

ASKER

could you please just tell me what I need to do because I can't be bothered reading all the crap on the link.
> 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
Avatar of adam8

ASKER

So how do I know when the user scrolls?
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
   
Avatar of adam8

ASKER

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 :-)
That's good. Does it work? I couldn't test it with your code of course ;-)
Avatar of adam8

ASKER

it is in a loop, everything is freezed until the loop completes so your code wouldn't really do much.
Avatar of adam8

ASKER

wait a minute. WIll your code stop the program from scrolling to the start of the list every time?
Avatar of adam8

ASKER

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.
Avatar of adam8

ASKER

any other suggestions
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.
Avatar of adam8

ASKER

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
Avatar of adam8

ASKER

It may seema bit confusing but the code that is making it scroll is this

flb.ListItems(i).Icon = Icons(i)
Avatar of adam8

ASKER

this is not going anywhere
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.
Avatar of adam8

ASKER

thanks
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
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

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.
Avatar of adam8

ASKER

I will be happy to give it a try. How did you know that making the listview invisible when hooking would work?
Avatar of adam8

ASKER

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?
ASKER CERTIFIED SOLUTION
Avatar of abel
abel
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
> 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.
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
Avatar of adam8

ASKER

I am going away for a 2 days but I will give it a go when I get back.

THanks for your hard work.
Avatar of adam8

ASKER

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.

> 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