Does anyone have any sample code or pointer to a helpful article for doing this - dragging one row of a listbox control to another position in the control and dropping it there, to re-order the items?
Thanks.
Does anyone have any sample code or pointer to a helpful article for doing this - dragging one row of a listbox control to another position in the control and dropping it there, to re-order the items?
Thanks.
OK, now I have a working DataWindow solution based on a very simplified version of http://anvil-of-time.com/wordpress/powerbuilder/powerbuilder-datawindow-dragdrop-rows-with-business-rules/
I created a user object DW u_drag_dw. It has the DragIcon set to DataPipeline! (Maybe there are better options.)
The DataWindow to be inserted into it has to have a 1-char String column (not displayed) named row_drag, a horizontal line at the very top of the Detail area that is invisible, with a DW expression for Visible that is IF (row_drag = 'T',1,0)
, and a horizontal line at the very bottom of the Detail area that is invisible, with a DW expression for Visible that is IF ( row_drag = 'B',1,0)
. Those lines will appear as part of moving the mouse to show where you are dropping the row.
Declare this Local External Function:
SUBROUTINE SleepMs(ulong milliseconds) LIBRARY "kernel32.dll" Alias for "Sleep"
Declare these Instance variables:
boolean ib_drag, ib_mouse_down
long il_dragged_row, il_mouse_down_x, il_mouse_down_y
Clicked event script:
this.SelectRow(0, FALSE) // unselect all selected rows
this.SelectRow(row, TRUE) // select the clicked row
ib_drag = (row > 0) // are we capable of dragging?
il_dragged_row = row // source row which is dragged (used for visual indicator on dw)
IF ib_drag THEN
ib_mouse_down = TRUE // button is clicked
il_mouse_down_x = xpos // original coordinates of pointer
il_mouse_down_y = ypos
ELSE
ib_mouse_down = FALSE
END IF
Create event ue_dwnmousemove, mapped to pbm_dwnmousemove, with script:
IF ib_drag THEN // we are capable of being dragged
IF ib_mouse_down THEN // mouse is down
IF (Abs(PointerX() - il_mouse_down_x) > 50) OR (Abs(PointerY() - il_mouse_down_y) > 50) OR &
(PointerX() = 0) OR (PointerY() = 0) THEN
// we have moved the pointer more than 50 powerbuilder units so we are dragging the row
Drag(Begin!)
END IF
END IF
END IF
DraggedWithin event script:
// scroll the datawindow if not all row fit
long l_i, ll_firstrow, ll_lastrow
ll_firstrow = long(this.Object.DataWindow.FirstRowOnPage)
ll_lastrow = long(this.Object.DataWindow.LastRowOnPage)
IF (row = ll_firstrow OR row = ll_firstrow + 1) AND ll_firstrow > 1 THEN
this.ScrollPriorRow( )
SleepMS(100) // don't scroll too fast!
ELSEIF (row = ll_lastrow OR row = ll_lastrow - 1) AND ll_lastrow < this.rowcount( ) THEN
this.ScrollNextRow( )
SleepMS(100)
END IF
// set the visual indicator for the drop location, by setting the column value that determines the
// visibility of the lines at the top and bottom of each row
FOR l_i = 1 TO this.RowCount()
this.SetItem(l_i, "row_drag", " ")
NEXT
// set up for visual indicators
IF il_dragged_row > Row THEN
this.SetItem(Row, "row_drag", "T")
IF Row <> 1 THEN
this.SetItem(Row - 1, "row_drag", "B")
END IF
ELSE
this.SetItem(Row, "row_drag", "B")
IF Row <> this.RowCount() THEN
this.SetItem(Row + 1, "row_drag", "T")
END IF
END IF
Create event ue_lbuttonup, mapped to pbm_dwnlbuttonup, with script:
long l_i
ib_Mouse_Down = False
IF ib_drag THEN // are we dragging?
ib_drag = False // stop dragging
This.Drag(End!)
END IF
// reset the visual indicator on the datawindow
FOR l_i = 1 TO this.RowCount()
this.SetItem(l_i, "row_drag", " ")
NEXT
DragDrop event script:
long ll_row, ll_beforerow
if source <> this then return
ll_row = GetSelectedRow(0) // should be same as il_dragged_row
IF ll_row = 0 THEN return
IF ll_row = row THEN return
if row > ll_row then
ll_beforerow = row + 1
else
ll_beforerow = row
end if
RowsMove(ll_row, ll_row, Primary!, this, ll_beforerow, Primary!)
ll_row = GetSelectedRow(0)
this.ScrollToRow(ll_row)
That's it! Place that object on a window, assign it a DataObject as explained above (with the row_drag column and the two lines), and it should just work. The one thing I don't like is that it is a bit flaky about scrolling at the top or bottom, you sometimes have to jiggle the position a bit before it really works. (I added the SleepMS calls to the code in the original article, because otherwise I found the whole DW scrolled at once, all the way to the top or bottom!)
I got this to more less work with a ListView control (with no icons) with a variation of the instructions in the PB Help topic "Moving items using drag and drop", which is for TreeViews.
Create a ListView on a window. Set it to ListViewReport! View style, with ShowHeader turned off. (I suppose it would be OK with a header.) Turn on DragAuto in the Other properties, but don't select a DragIcon (which means it will show a replica of the item when dragging, which is good!).
After whatever code Adds the items to the ListView (or I guess if they are set in the painter), add the following line of code in the window, to make sure only one column of items shows:
lv_1.AddColumn("", Left!, lv_1.Width - 100) // if I don't subtract 100, I get a horizontal scroll bar I don't want
Add an int instance variable ii_dragged_index.
ListView BeginDrag script:
ii_dragged_index = index
ListView DragDrop script:
ListViewItem lvi_src
this.GetItem(ii_dragged_index, REF lvi_src)
if this.InsertItem(index + 1, lvi_src.Label, 1) = -1 then
MessageBox("Error", "Internal error: Could not drop item.")
return 0
else
if ii_dragged_index > index then
this.DeleteItem(ii_dragged_index + 1)
else
this.DeleteItem(ii_dragged_index)
end if
end if
I did not include a version of the DragWithin script that that Help topic shows, which sets the DropHighlighted property on each item as you drag over it, because at least in my version of it, it left a bunch of items highlighted, and I saw no ListView API to turn off that highlighting. And it didn't seem necessary.
HOWEVER, the problem with this solution is that if you have more items than fit in the ListView (so there's a vertical scroll bar), dragging an item down to the bottom does not cause scrolling! So I can only drag to the bottom of the currently displayed items, then scroll the view, then drag the item some more. Not wonderful.
I'm Googling and reading Help and starting to think this might be very difficult (to impossible) with a ListBox.
One option seems to be using a DW with a simplified version of the solution here:
It also looks a bit like a ListView might work, using the same sort of techniques as the Help lists for a TreeView, in the Help topic you can find in the Index under "Moving items using drag and drop".
Thoughts? Thanks.