X&Y of a listbox row

How does someone find the X&Y of a selected row in a Listbox without clicking on the row? There is probably a very easy way but it eludes me tonight…

thanks!

X would be anywhere on the row… I assume you want to create a drag item?

Anway:

[code]Dim Y as Integer

If LB.Border Then Y = 1
If LB.HasHeading Then Y = Y + LB.HeaderHeight
Y = Y + (LB.ListIndex - LB.ScrollPosition)*LB.RowHeight
[/code]
Y should be at the top of the row.

Alternately

Dim Y as integer = -1, ub as integer = LB.Height - 1 For i as integer = 0 to ub If LB.RowFromXY(10,i) = lb.ListIndex Then Y = i Exit End if Next

Karen

that was perfect. And I’m opening a graphic at the coordinates related to the selected row.

I knew someone smart like yourself would know this off the top of their heads.

I like Karen’s second version a lot, but I think it can be more efficient using a binary search. Let me try something…

This will give you the precision of Karen’s second version with fewer loops, especially if the selection is near the bottom. I tested it in the Change event so adjust as necessary.

  if me.ListIndex = -1 then
    lblY.Text = "-1"
    return
  end if
  
  dim h as integer = me.Height -1
  dim y as integer = h \\ 2
  dim highY as integer = h
  dim lowY as integer = 0
  
  do 
    dim r as integer = me.RowFromXY( 1, y )
    if r = me.ListIndex then
      exit
      
    elseif r < me.ListIndex then
      lowY = y
      y = y + ( ( highY - y ) \\ 2 )
      
    else // r > me.ListIndex
      highY = y
      y = y - ( ( y - lowY ) \\ 2 )
      
    end if
  loop
  
  // Find the top of the row
  highY = y - 1
  for y = highY downto 0
    if me.RowFromXY( 1, y ) < me.ListIndex then
      y = y + 1
      exit
    end if
  next y
  
  lblY.Text = str( y )

I don’t understand why there’s a second version. Doesn’t the first directly compute the correct Y?

Good question. It turns out, it does if there is a header. If there isn’t a header, it’s off by one. So without a header, the Y of the first row is 1 whereas the calculation will return 0. Each subsequent row is off by one too.

This means that if you use RowFromXY( 1, 0 ), you will not get back -1, not 0. I don’t know if this is a bug or not, but there you go.

(The line in the post above should have read “you will get back -1, not 0”. Need sleep…)

Here is my current test code. It has additional checks to prevent an endless loop.

  
  if me.ListIndex = -1 then
    lblY.Text = "-1"
    return
  end if
    
  dim h as integer = me.Height - 1
  dim y as integer = h \\ 2
  dim highY as integer = h
  dim lowY as integer = 0
  
  do 
    dim r as integer = me.RowFromXY( 1, y )
    if r = me.ListIndex then
      exit
      
    elseif r < me.ListIndex then
      if y = lowY then exit
      lowY = y
      y = y + ( ( highY - y ) \\ 2 )
      
    else // r > me.ListIndex
      if y = highY then exit
      highY = y
      y = y - ( ( y - lowY ) \\ 2 )
      
    end if
  loop
  
  // Find the top of the row
  highY = y - 1
  for y = highY downto 0
    dim r as integer = me.RowFromXY( 1, y )
    if r < me.ListIndex then
      y = y + 1
      exit
    end if
  next y
  
  // Compare
  'dim calcY as integer = ( me.ListIndex * me.RowHeight ) + if( me.HasHeading, me.HeaderHeight, 0 )
  'if y <> calcY then
  'break
  'end if
  
  lblY.Text = str( y )

That’s the border insetting all sides by 1. Turn border off and RowFromXY(0, 0) = 0. It happens at the bottom too. With Border True the last Y pixel (lb.Height-1) is not counted as a cell so RowFromXY returns -1.

The code I wrote here was off the top of my head without testing or optimization to illustrate how to do it , but I think the first version is correct. I think your off by one comes from not considering the border, which my code above does.

I included the second way as an afterthought because years ago the listbox did not have either the RowHeight (only DefaultRow Height which returns -1 if not set) or HeaderHeight properties so I had to use RowFromXY to get the header height and row height.

BTW IMO IF Listbox.HasHeader is false, I think LB.headerHeight should return 0, but it returns the same value regardless, necessitating the If statement… Which is very unintuitive

  • Karen

I used the first version and havent found it to be off yet. So as far as I can tell it works as designed/expected.