hangover


I have a control consisting of two listboxes which allow the user to drag and drop items from the left listbox to the right listbox to copy them across, or drag and drop items within the right listbox to change their order.

I have got the mechanics of it working reasonably well using the OLEDragDrop methods of the listboxes, however it is not as slick as I would like. There are three improvements I would like to make and would gratefully appreciate any help or advice:

1) My algorithm for identifying which item in the listbox the cursor is over has been formulated through trial and error (using the FONTMETRIC function) and is not wholly reliable. Is there a 100% accurate means to determine this

2) Better yet, is there a way of highlighting the listbox item that the cursor is dragging over so that the user can see exactly where the dragged item will be dropped

3) Also, is there a standard DragIcon to use for listbox items and, if so, where can I obtain it

Many thanks.





Re: Drag and Drop with Listbox

Alex Feldstein


Have you seen the sample provided with VFP

Open the Task Pane Manager, go to Solution Samples | Controls | List Box and try the "Move items between listboxes" example. It may answer all your questions.

See:

http://msdn.microsoft.com/library/default.asp url=/library/en-us/dv_foxhelp9/html/ceef4ee0-0b13-498a-ad2b-33c095d86724.asp frame=true






Re: Drag and Drop with Listbox

hangover

Many thanks Alex, I'll check it out.





Re: Drag and Drop with Listbox

hangover

I've finally had a chance to check out the sample and it is a much more elegant solution than my slightly Heath Robinson approach.

I do have a couple of reservations about this method however:

  • it entails first dragging an item to the listbox where it is appended to the end and then using the MoverBars to move it to the desired position - it would be preferable if the user could drag it directly to the position where they wanted it to be inserted;
  • also, the moverbars property (which somehow I had never come across before) is only available if the RowSourceType property is set to 0 or 1 and so cannot be used in conjunction with listboxes which display more than one column.

Hence I may still persevere with perfecting my algorithm for determining which item in a listbox the mouse is over. If anyone can offer any help with this I would be grateful.






Re: Drag and Drop with Listbox

AndyKr

>> Hence I may still persevere with perfecting my algorithm for determining which item in a listbox the mouse is over. If anyone can offer any help with this I would be grateful

What do you mean If you use the native methods you always know which is the source object. I use this code in the DragDrop event of a listbox class (my form has FOUR lists AND a Treeview) and users can drag from one to the other:

Code Snippet

LPARAMETERS oSource, nXCoord, nYCoord

IF oSource.Class # THIS.Class

*** Hand off to the form method passing both Source and Destination objects

ThisForm.DoDragDrop( oSource, This )

ENDIF

The form method handles all the necessary interaction and any additional processing like this:

Code Snippet

***********************************************************************

* Program....: FRMMANAGEPLANS.DODRAGDROP

* Purpose....: Control method for responding to drag actions from the Tree

***********************************************************************

LPARAMETERS toSource, toTarget

LOCAL lcTarget, loNode, lnLevel, lcDropZone, lcLevel, lcBelow, lcTable, lcWhere, lcMsg, lnPK, llOk

LOCAL lcDesc

*** Assume Success

STORE .T. TO llOk

IF VARTYPE( toTarget ) = 'O'

*** Called with a drop target, and, therefore, a Source Object

lcTarget = LOWER( ALLTRIM( toTarget.Name ))

ELSE

*** Called from the button, so neither source or target

*** Set lcTarget = '' so that ANYTHING compared to it returns .T.!

lcTarget = ''

ENDIF

So at this point you know both the Source Object (and hence can get any of its properties, and the intended Target object (and so can get any of its properties).

I also have code in the DragOver events which calls another form method to handle changing the icon so that the user know whether this is a valid target object for the drop

Code Snippet

LPARAMETERS oSource, nXCoord, nYCoord, nState

ThisForm.ChangeIcon( oSource, nState )

Again, you have a reference to the Source object and in that method all I do is:

Code Snippet

LPARAMETERS toSource, tnState

LOCAL loTarget

loTarget = SYS( 1270 )

IF VARTYPE( loTarget ) = 'O'

IF loTarget.Class = toSource.Class

*** Only allow drop between controls of different classes

toSource.DragIcon = This.cNoDropIcon

ELSE

IF tnState = 0

*** Entering a DD enabled control

toSource.DragIcon = This.cDropIcon

ELSE

IF tnState = 1

*** Leaving a DD enabled control

toSource.DragIcon = This.cNoDropIcon

ENDIF

ENDIF

ENDIF

ENDIF

You may want to change the 'disallow' line (since you are allowing drag from the same Class), but the basic methodology is the same - you need to check the source to see if you want to allow dropping from that source on the target and you get the target by using SYS(1270) to see what is actually under the mouse.

It's really very simple....






Re: Drag and Drop with Listbox

hangover

>>What do you mean If you use the native methods you always know which is the source object

Many thanks Andy, there's some very helpful stuff in those code snippets. However, the issue I have is not identifying the source or target object (i.e. listbox), it is identifying the listitem within the target listbox that the mouse is over. I want the user to be able to drag a listitem from the source listbox to the target listbox and insert it directly before the listitem in the target listbox that the mouse pointer is over. (And ideally, I would like this listitem to be highlighted as I drag the pointer over it).






Re: Drag and Drop with Listbox

AndyKr

>> it is identifying the listitem within the target listbox that the mouse is over. I want the user to be able to drag a listitem from the source listbox to the target listbox and insert it directly before the listitem in the target listbox that the mouse pointer is over.

I think you'll be out of luck there. The only way you can know what item is 'selected' is to select it first. To position it explicitly you need to use the ListIndex property of the item. But to do that you have to shuffle all the others to make room...NOT a trivial task!






Re: Drag and Drop with Listbox

hangover

I've got it working perfectly now. In case it is of use to anyone else, I've put my solution below (I'm sure there are much better ways of doing it but, hey, it works!)

My control consists of a container object containing two listboxes, lstAvailable and lstSelected, which allow me to either copy a ListItem from lstAvailable to lstSelected or move a ListItem within lstSelected.

Within the OLEStartDrag event of each listbox I store the current ListIndex property of lstSelected, i.e.

Code Snippet

THIS.Parent.nSelectedIndex = THIS.Parent.lstSelected.ListIndex

Then within the OLEDragOver event of lstSelected I can identify and highlight the ListItem I am currently dragging over with ...

Code Snippet

LPARAMETERS oDataObject, nEffect, nButton, nShift, nXCoord, nYCoord, nState

LOCAL loVFPSourceObject, lnListIndex

loVFPSourceObject = oDataObject.GetData ("VFP Source Object")

IF INLIST (LOWER (loVFPSourceObject.Name), "lstavailable", "lstselected")

WITH THIS

lnListIndex = CEILING ((nYCoord - AbsolutePosition ())/(FONTMETRIC (1, .FontName, .FontSize) + FONTMETRIC (4, .FontName, .FontSize) - 1)) + .TopIndex - 1

DO CASE

CASE BETWEEN (lnListIndex, 1, .ListCount)

.ListIndex = lnListIndex

CASE NOT EMPTY (.Parent.nSelectedIndex)

.ListIndex = .Parent.nSelectedIndex

ENDCASE

ENDWITH

ENDIF

Code Snippet

FUNCTION AbsolutePosition

LPARAMETERS tnTop, tnLeft

IF PROPER (.BaseClass) <> "Form"

IF TYPE (".Top") = "N"

tnTop = EmptyValue (tnTop, 0) + .Top

tnLeft = EmptyValue (tnLeft, 0) + .Left

ENDIF

WITH .Parent

= AbsolutePosition (@tnTop, @tnLeft)

ENDWITH

ENDIF

RETURN EmptyValue (tnTop, 0)

FUNCTION EmptyValue

LPARAMETERS teValue1, teValue2

RETURN IIF (EMPTY (teValue1), teValue2, teValue1)






Re: Drag and Drop with Listbox

AndyKr

Cool! Glad you got it working!






Re: Drag and Drop with Listbox

deepak_.NET

Can you please post complete code for drag and drop between 2 listbox lstAvailable and lstSelected . I am also stuck in same problem. Your help is greatly appreciated.





Re: Drag and Drop with Listbox

nautic20

Does anyone know how to translate this to C#