Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

listbox - automatically center selected item

Status
Not open for further replies.

ciumes

Programmer
Feb 11, 2005
10
US
I have a listbox with a hundred or so items. When the user clicks on the top or bottom item, I would like to automatically scroll the listbox so the selected item is centered.

When using the up/down arrows, the function works as desired, firing the Click event, where I change the TopIndex value to move the selected item to the center of the listbox.

However, when simply clicking the top/bottom item, the listbox is scrolled twice. My selected item ends up just above the top or just below the bottom of the listbox. I have included the code below, as it's not very long. Any help much appreciated.

Code:
Private Sub lbLST_Click()
  Dim i%, f, l2%
  Const lbLstLines = 16		' computed elsewhere, hardcoded here
  i = lbLST.ListIndex
  If i < 0 Then Exit Sub
' Now we want to adjust the topIndex so if we clicked on the top
' or bottom entry, the listbox automatically centers the
' selection. That way we don't have to use the scrollbar as often.
' Works with up/down arrows, but not with mouse clicks!
  l2 = lbLstLines / 2   ' half the lines displayed
  Select Case lbLST.ListIndex
  Case lbLST.TopIndex
    noLstClick = True
    If lbLST.TopIndex >= l2 Then
      lbLST.TopIndex = lbLST.ListIndex - l2
    Else
      lbLST.TopIndex = 0
    End If
  Case lbLST.TopIndex + lbLstLines - 1
    noLstClick = True
    If lbLST.ListIndex + l2 < lbLST.ListCount Then
      lbLST.TopIndex = lbLST.TopIndex + l2
    Else
      lbLST.TopIndex = lbLST.ListCount - 1
    End If
  End Select
End Sub
 




Hi,

"Centered" means something very specific in Excel: Either Horizontal or Vertical Alignment.

Are you referring to REPOSITIONING the item to occur half way thru the list?
Code:
  On Error Resume Next
  Select Case i
    Case 0
      noLstClick = True
       lbLST.AddItem lbLST.List(0), l2
      lbLST.RemoveItem 0
    Case lbLST.ListCount - 1
      noLstClick = True
      lbLST.AddItem lbLST.List(lbLST.ListCount - 1), l2
      lbLST.RemoveItem lbLST.ListCount - 1
  End Select
seems to work for me.

Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 
My apologies for sloppy use of terms. Yes, I mean to reposition the selected item. I am not adding new items, just selecting various items. When the top or bottom item is selected, I want to reposition it to the middle of the displayed lines. Using the up/down arrow keys works fine; clicking the mouse on the top or bottom item fires the Click event twice. So, if TopIndex is 40 and I click on the top item, I get two events: one with TopIndex=40 and ListIndex=40 (just what i want) and then another Click event after I change TopIndex to 32, where TopIndex=24 and ListIndex=32, moving my selected item (ListIndex=40) just past the bottom of the 16-line listbox.

I was wondering whether changing TopIndex fired the event, but using the up/down arrow keys runs the same code, and I just get one click event.

Thanks for the quick reply.
Steve

(PS - the noLstClick variable in my code can be discarded - not used for the purpose of this example.)
 




So is there still a question?

Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 
Ummm, yes. Let's try it this way:

The only thing my Click-event handler does is change the Topindex for a listbox, so that I can reposition the selected item to the center of the listbox.

Question:
How come I get one Click event if I select the item via up/down arrow keys, but I get two Click events if I select using the mouse? Can I get around this, or just document it as a feature? The entire event handler is:

Code:
Private Sub lbLST_Click()
  Dim i%, f, l2%
  Const lbLstLines = 16
  i = lbLST.ListIndex
  If i < 0 Then Exit Sub
  l2 = lbLstLines / 2   ' half the lines displayed
  Select Case lbLST.ListIndex
  Case lbLST.TopIndex
    If lbLST.TopIndex >= l2 Then
      lbLST.TopIndex = lbLST.ListIndex - l2
    Else
      lbLST.TopIndex = 0
    End If
  Case lbLST.TopIndex + lbLstLines - 1
    If lbLST.ListIndex + l2 < lbLST.ListCount Then
      lbLST.TopIndex = lbLST.TopIndex + l2
    Else
      lbLST.TopIndex = lbLST.ListCount - 1
    End If
  End Select
End Sub

my listbox happens to be 16 lines tall, hence the value for lbLstlines=16.
 


Why does this matter?

Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 
hey. I'm asking the questions here. If you can't help, don't worry about why it's important to me.
 
ciumes, if you post here then YOU get to be asked questions as well. That is the way it is played. I suggest you look at the FAQ on posting. Your response was borderline rude.

Questions on "why" are legitimate and should be answered with good grace. They are asked for a reason. The reason we ask "why" questions is because very very often people post situations that can be resolved with a different perspective. Perhaps what they really want can be achieved not by fixing their posed question, but by doing things completely differently.

I am not saying this is the case for your question, but your response is not helpful in getting positive assistance. Skip is a top member here - see the list of MVP's for this forum. That is not a gratuitous listing. It means that literally thousands of people have felt he has helped.

Be nice. Be reasonable. If you truly truly feel that you do not need to ever answer any questions, then perhaps another forum may be appropriate.


faq219-2884

Gerry
My paintings and sculpture
 
Well, I appreciate your response. I could tell right away that Skip was knowledgeable and helpful. My somewhat acerbic posting was a bit out of line, and I apologize, Skip, for not at least including a smiley or something. But do try to look at it from my perspective.

I post a question.
Skip posts a response which, while helpful, did not answer the question.
I then try to clarify, and he asks whether I still have a question.
I try to clarify further, and the next response is, "Why do you care?"

I care because I wanted the user interaction to work in a particular way, and the API wasn't acting the way I expected. That's all - just looking for an explanation or a workaround. I was just a bit surprised at such a shrug from someone who obviously knows a lot and also cares.
 




I am trying to discover HOW the code is adversely affecting the interface. What is actually happening that makes it NOT work?

I did notice, when I was testing my code, that the CHANGE in the control, fired the Click event. I stuck the On Error statement to circumvent at least one of the effects of that recursiveness.

Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 




"Skip posts a response which, while helpful, did not answer the question."

How did my code NOT answer the question? It places either the FIRST entry or LAST entry in the middle of the list.




Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 
Yes, by adding/deleting an item. I wasn't trying to change the contents of the list, just scroll through it.

Interesting catch, Skip, about the OnError. I wasn't aware that that had anything to do with firing events. I did know that Application.Enableevents had no effect on UserForms, but didn't think of OnError. So, did that inhibit the 2nd Click event for the listbox? That'd be cool. I'll play around with it a bit. And thanks for not sulking at my rudeness.
 


No problem!

On Error does NOT have anything to do wit Firing events. But in this case, when the event fires from the removeitem.

BTW, your code does absolutely NOTHING to the list.

Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 
That is correct. I do not want to change the list. I just want the user not to have to use the scrollbar to traverse the list if they're doing short hops. If they're checking basically sequential entries, then clicking the bottom one displayed will scroll up so it is in the center of the window, so they can see what comes before and after. Sometimes the scroll bar can be hard to use if the list is long. Moving the cursor a small amount can shift the displayed list by large increments. I was just trying to make it easier to stay local. click click click, and if the selected item happened to be the first or last, then a modest local scroll would take place automatically.

The only reason the topic came up was that the up/down arrow keys worked fine, but the mouse click did not.

I guess this horse is truly dead. No matter how hard I beat it, it won't get up. Thanks for your input.
 



One strategy might be to re-structure the LONG list into a list/sublist with two controls. The result of the FIRST click is the source for the second control, by way if a parameter query.

Skip,
[sub]
[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue][/sub]
 
ciumes said:
I care because I wanted the user interaction to work in a particular way, and the API wasn't acting the way I expected. That's all - just looking for an explanation or a workaround. I was just a bit surprised at such a shrug from someone who obviously knows a lot and also cares.
The point is that Skip's question was not a shrug. It was a legitmate question that you answered here. It matters because you want it to work in a particular way, for a particular reason.

It seems to me there is a slight confusion regarding events.

If you use the keyboard to Tab into a control, the _Click event does NOT fire. The control has focus. If you change anything - say with the keys - THEN the _Change events fires, followed by the _Click event.

If you click into a control so it gets focus there, _Change still fires first, followed by _Click.

In other words, if you do anything with a control, _Change fires, followed by _Click. You can see this well if you play with:
Code:
Private Sub ListBox1_Change()
   MsgBox "From Change"
End Sub

Private Sub ListBox1_Click()
   MsgBox "From Click"
End Sub

I am trying to follow what is possible.

"When the user clicks on the top or bottom item, I would like to automatically scroll the listbox so the selected item is centered."

If I click on the bottom item - and by that I assume you mean the last item - I can not make it centered by any means at all. Are you saying you can? How? It is the last item in the list. How can you get it centered in the visible control if there are no other items following?

Ditto, if I select the first item (ListIndex = 0). It seems you are saying that you can center that in the visible control. How? There are no items preceding it.

I can center any selected item, except if that item is close to the start, or the end, of the list. I am using _Change, rather than _Click to action things. That way, it does not matter if I click with the mouse, or use the keyboard.

I am missing something. What?

faq219-2884

Gerry
My paintings and sculpture
 
Hi all,

I found ciumes's question to be a very interesting one, and I have been pecking away at the problem for the last few days.

Let me paraphrase what I believe ciumes is trying to do here:

A listbox only displays a portion of a list. The size of the portion depends on the height of the listbox. Say you have 100 items in your list, but only 7 displayed at any given time in the listbox. Ciumes wants to be able to click on the last item *displayed* in the listbox (as opposed to the last item in the list), and have the display change accordingly so that the item clicked now shows as the fourth item *displayed*. To put numbers to this: let the current TopIndex = 4, and the ListIndex for the last number *displayed* = 10. When that last item displayed is clicked, ListBox1.ListIndex = 10. Ciumes wants the listbox to change so that ListBox1.TopIndex = 7.

Ciumes, you'll correct me if I've misinterpreted what you're trying to do.

Here's my simplified version of ciumes's code. I set myself up a userform with a listbox (ListBox1), and textboxes to display the current ListIndex and TopIndex values.

Code:
Private Sub ListBox1_Click()
    Select Case ListBox1.ListIndex
    Case Is < halfway
        ListBox1.TopIndex = ListBox1.ListIndex
    Case Else
        ListBox1.TopIndex = ListBox1.ListIndex - halfway + 1
    End Select
   
    TextBox1.Text = ListBox1.TopIndex
    TextBox2.Text = ListBox1.ListIndex
End Sub

In UserForm_Initialize() I set halfway = 4.

As ciumes mentioned, this works fine when the user uses the keyboard to move through the listbox. The trouble comes when the user clicks with the mouse.

I've noticed that when I hold down my mouse button over the item I'm selecting, the code works as it should. It's when I take my finger *off* the mouse button that the code fires again, moving the item that my mouse pointer was hovering over to the middle of the listbox. This doesn't happen when I step through the code line by line, just when I'm running it normally.

My question now, which I think will solve ciumes's problem, is: how to keep the code from firing again when the user releases the mouse button? Is there something we can put into the MouseUp event to fix this? (I've tried, but I haven't been able to crack it myself.)

-LB
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top