views:

1080

answers:

4

I have a TListBox with multiselect and ExtendedSelect both set to true. I need to be able to drag multiple items in the list box to re-arrange them. My problem is what happens when the user clicks on an item that is already selected without holding down the CTRL or SHIFT key.

Case 1: DragMode is set to dmManual The selection is cleared before the mouse down. This will not allow multiple items to be dragged.

Case 2: DragMode is set to dmAutomatic The MouseDown event never fires. The selection is not cleared so dragging is OK, but the user cannot clear the selection by clicking on one of the selected items. This really causes a problem if all the items are selected or the next item the user wants to select was part of the current selection.

Note that this problem only happens if you assign something to the DragObject in the OnStartDrag procedure. I think the problem would go away if OnStartDrag would only start after the user moves the mouse. I have Mouse.DragImmediate := false set but I still get the StartDrag fired as soon as I click on an item in the list box.

I am using Delphi 7 for this project but I see the same behavior in Delphi 2007.

A: 

I have played with this for a while. And observe the same effects.

I would use Case2 and add a (Select All/Deselect All) button to the list. It even adds extra functionality and solves the most annoying part of the problem.

Gamecat
A: 

I'm not sure why this makes a difference but if I change the DragObject to be a TDrag**Control**ObjectEx (instead of a TDragObjectEx) I get the behavior I am looking for. Drag mode is set to Automatic.

I tried to look and see what this was affecting but I could not figure it out.

Mark Elder
A: 

Use Case 2 and when the TListBox.OnMouseUp event fires check to see if multiple items are selected and were dragged. If multiple items are selected, but weren't dragged, then deselect all items apart from the clicked item.

I would use this method because Windows Explorer works this way.

A: 

Bit of a kludge but this works. DragMode on the ListBox is set to dmAutomatic.

procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  iDropIdx, i: Integer;
  pDropPoint: TPoint;
  slSelected: TStrings;
begin
  {Which item is being dropped onto?}
  pDropPoint := Point(X, Y);
  iDropIdx := ListBox1.ItemAtPos(pDropPoint, False);

  slSelected := TStringList.Create;
  try
    {Copy the selected items to another string list}
    for i := 0 to Pred(ListBox1.Items.Count) do
    begin
      if (ListBox1.Selected[i]) then
        slSelected.Append(ListBox1.Items[i]);
    end;

    {Find the selected items in the listbox and swap them with the drop target}
    for i := 0 to Pred(slSelected.Count) do
    begin
      ListBox1.Items.Exchange(ListBox1.Items.IndexOf(slSelected[i]), iDropIdx);
      inc(iDropIdx);
    end;
  finally
    slSelected.Free;
  end;
end;
_J_