# 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
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.