Add Drop Position Indicator in Listbox

Hey guys,

If you drag an item in a listbox to a different row in the list box, the Xojo framework gives you a nice black line that indicates where the current drop position will be when you release the mouse.

However, if you are dragging an item into the listbox from the outside, you get no such indicator line. You can see the outline of the drag item but no line indicating where it will be dropped. I’ve been struggling trying to draw my own and I’ve not had much success. I either get no line drawn or sometimes I get a line drawn in every listbox cell. And then I want to only create this line when dragging from the outside since the framework is doing it for me for internal drags.

So instead of continuing to beat my head against the wall, I thought I’d ask here…Someone else must have done what I’m trying to do…

I am doing that on Mac and it may be different on Windows, but the principle remains.

Normally, such thing is done in DragOver but it simply does not work. And MouseOver does not either.

Here is what I did, which can be refined, but it works :

  • Add to the window a 1 pixel high Rectangle the same width as the Listbox
  • Make its left minus its width ; that will make it invisible
  • Add a 100 Ms multiple timer to the window
  • Add this Action event to the timer :

Sub Action() if system.mousedown then Rectangle1.left = ListBox1.left Rectangle1.Top = system.Mousey-self.top else Rectangle1.Left = -500 end if End Sub

Add these events to the ListBox :

Sub Open() Timer1.Enabled = False End Sub

Sub MouseEnter() Timer1.Enabled = True End Sub

Sub MouseExit() Timer1.Enabled = False Rectangle1.Left = -(Rectangle1.Width) End Sub

The 1 pixel rectangle will follow the cursor tip.

That is not quite identical to the way reorder drag works, where the line jumps from one row to another. I quickly put together the proof of concept. It should not be very difficult to calculate based on ListBox.RowHeight where to have the line jump, and you will have a nice emulation.

dragline.xojo_binary_project

Yes it does… I do it all the time, but you need to invalidate teh row you are over and do the drawing in CellBackgroundPaint.

  • Karen

+1 for Karen and the method. In DragOver you get the data and then in CellBackgroundPaint you do the painting of a nice RoundRect if drag data is available for the specific row.

[quote=181398:@Karen Atkocius]Yes it does… I do it all the time, but you need to invalidate teh row you are over and do the drawing in CellBackgroundPaint.

  • Karen[/quote]

I am sorry, but when dragging a file from the desktop over the listbox, the DragOver event does not even fire here. 2015R2, Mac OS X 10.10.3. I do not see how I could invalidate anything from an event that does not occur.

MouseDrag does not fire in that case either.

That said, with the technique I use to track the position of the drag, it is perfectly possible to draw in CellBackGroundPaint using RowFromXY and have it display at the bottom of the cell akin to what happens in reorder drag.

DragOver will fire I’ve been messing with it. You have to make sure all your dragging parameters like AcceptTextDrop, etc. are set up.

So thank you Karen and Beatrix. The big part was I was missing InvalidateCell in the DragOver event.

So I’m able to draw my line. Problem is I need to remove the line when I’m out of the cell. So I haven’t figured that out yet… using ClearRect ends up clearing too much.

OK. So here’s what I have…

DraggingRow is the row being returned from calculating it from X,Y in the DragOver event. “DraggedRow” is a row value that is returned in the DragRow event when grabbing a row in the listbox and moving it. I don’t want my line painted in that case.

I am painting a line in white on the previous row to remove the black. Is that the best way to do it?

So any pointers would be helpful.

Function CellBackgroundPaint(g As Graphics, row As Integer, column As Integer) As Boolean
  If DraggingRow > -1 and DraggedRow = -1 Then
    
    Dim X1 as integer = 0
    Dim Y1 as integer = 1
    Dim X2 as integer = me.Width
    Dim Y2 as integer = Y1
    g.PenWidth = 2
    
    If row = DraggingRow Then
      g.ForeColor=&c00000000
    Else
      g.ForeColor = &cFFFFFF00
    End If
    g.DrawLine(X1,Y1,X2,Y2)
  End If
End Function

[code]
Function DragOver(x As Integer, y As Integer, obj As DragItem, action As Integer) As Boolean
Dim OldDraggingRow as integer = DraggingRow

DraggingRow = me.RowFromXY(x,y)

If OldDraggingRow <> -1 Then
me.InvalidateCell(OldDraggingRow,0)
End If

me.InvalidateCell(DraggingRow,0)
End Function

[code]

Stupid website won’t let me edit my post 1 second after posting so I can fix the code tag at the end. :frowning:

[quote=181405:@Michel Bujardet]I am sorry, but when dragging a file from the desktop over the listbox, the DragOver event does not even fire here. 2015R2, Mac OS X 10.10.3. I do not see how I could invalidate anything from an event that does not occur.

[/quote]

Ah I did not notice it was dragging a file… I do it dragging other things often. Never tried it with a file. Sounds like a bug/limitation in Xojo…Maybe due to not supporting promised Files? In any case that should be remedied by Xojo Inc.

  • Karen

[quote=181428:@Karen Atkocius]Ah I did not notice it was dragging a file… I do it dragging other things often. Never tried it with a file. Sounds like a bug/limitation in Xojo…Maybe due to not supporting promised Files? In any case that should be remedied by Xojo Inc.

  • Karen[/quote]
    Yeah I’m not dragging files but items from one listbox to another.

Jon

[quote=181411:@Jon Ogden]I am painting a line in white on the previous row to remove the black. Is that the best way to do it?

[/quote]

I just invalidate the old row IIRC. I Don;t have that code here to check… I can when I get home this eveneing

  • Karen

Thanks, Karen.

OK. Here’s my latest code from the CellPaint event. I think this looks quite close to what the framework gives you when dragging rows:

DraggingRow is the Row over which the drag is taking place.
DraggedRow is > -1 if you are dragging an existing row as opposed to moving a drag item into the listbox.

Function CellBackgroundPaint(g As Graphics, row As Integer, column As Integer) As Boolean
  If DraggingRow > -1 and DraggedRow = -1 and row = DraggingRow Then
    
    Dim o as New OvalShape
    
    o.BorderColor = &c00000000
    o.FillColor = &c00000000
    o.Fill = 0
    o.Width = 6
    o.BorderWidth = 1
    o.Height = 6
    o.Y = me.RowHeight-3
    o.X = 4
    o.Border = 100
    g.DrawObject(o)
    
    Dim X1 as integer = 6
    Dim Y1 as integer = me.RowHeight-3
    Dim X2 as integer = me.Width
    Dim Y2 as integer = me.RowHeight-3
    g.PenWidth = 2
    g.ForeColor=&c00000000
    g.DrawLine(X1,Y1,X2,Y2)
  End If
  
End Function