4

Is there a feature in the Listview control to shift items up and down?

Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
Kermia
  • 4,171
  • 13
  • 64
  • 105
  • According to title , Language is Delphi and according to the language , OS is Windows certainly. – Kermia Mar 13 '11 at 11:33
  • My apologies, I usually check the tags for these things. It is likely to be missed if you put it the end of the heading (in parentheses). – Marcelo Cantos Mar 13 '11 at 11:38
  • @Kermia: You forgot to say that you use the control in "report mode" (`ViewStyle := vsReport`). No, there is no such feature. You have to write one yourself. [This would be trivial if you used a `TListBox` instead, but I guess that you need the columns.] – Andreas Rejbrand Mar 13 '11 at 12:46
  • So , what is the solution for shift items ? would you give me an example ? – Kermia Mar 13 '11 at 13:24
  • Until I read the post I was thinking from the title that the question had to do with the key strokes `Shift-Up` and `Shift-Down`. The hyphens seem to be out of place here and very misleading. But I am not an English teacher, so I may just need to learn something new from the case. – Andriy M Mar 13 '11 at 20:22
  • @Andriy : Shift-Down is not similar to Shift **+** Down – Kermia Mar 14 '11 at 12:58
  • @Kermia: I know the latter pattern seems now to be established as the standard for specifying keystrokes, but I still remember very well the times when it wasn't so. `Shift-Up` was as correct and common a designation as `Shift+Up`. And for Control keystrokes you could additionally have `^Up`. Anyway, I've now learnt that 'shift-up' can very well be correct grammar for a noun. So the fault in being confused is all mine anyway. :) – Andriy M Mar 14 '11 at 13:21

2 Answers2

4

Not having worked with TListView very much (I mostly use database grids), I took your question as a chance to learn something. The following code is the result, it is more visually oriented that David's answer. It has some limitations: it will only move the first selected item, and while it moves the item, the display for vsIcon and vsSmallIcon is strange after the move.

procedure TForm1.btnDownClick(Sender: TObject);
var
  Index: integer;
  temp : TListItem;
begin
  // use a button that cannot get focus, such as TSpeedButton
  if ListView1.Focused then
    if ListView1.SelCount>0 then
    begin
      Index := ListView1.Selected.Index;
      if Index<ListView1.Items.Count then
      begin
        temp := ListView1.Items.Insert(Index+2);
        temp.Assign(ListView1.Items.Item[Index]);
        ListView1.Items.Delete(Index);
        // fix display so moved item is selected/focused
        ListView1.Selected := temp;
        ListView1.ItemFocused := temp;
      end;
    end;
end;

procedure TForm1.btnUpClick(Sender: TObject);
var
  Index: integer;
  temp : TListItem;
begin
  // use a button that cannot get focus, such as TSpeedButton
  if ListView1.Focused then
    if ListView1.SelCount>0 then
    begin
      Index := ListView1.Selected.Index;
      if Index>0 then
      begin
        temp := ListView1.Items.Insert(Index-1);
        temp.Assign(ListView1.Items.Item[Index+1]);
        ListView1.Items.Delete(Index+1);
        // fix display so moved item is selected/focused
        ListView1.Selected := temp;
        ListView1.ItemFocused := temp;
      end;
    end;
end;
crefird
  • 1,590
  • 11
  • 17
  • What about multi-select? Presumably Kermia doesn't need that. – David Heffernan Mar 13 '11 at 19:04
  • 1
    Also it's painful to have two practically identical routines. Write one routine and make it receive a parameter to determine whether it moves up or down. – David Heffernan Mar 13 '11 at 19:12
  • 1
    @David: I also dislike repeated code. When I try to help someone, my priority is to ensure the code works and is complete enough so it can be used with a minimum of assumptions. I felt I had achieved this goal, so I left refactoring up to the user. – crefird Mar 14 '11 at 04:02
  • The code does not work when the position is 1 in the listview. This is the flow for the code, when we click an up button the focus first shifts to the selected column -1 and then the KeyUp event handler is invoked. What is the work around for that? – siddharth taunk May 05 '16 at 09:16
3

You have two options:

  • Delete them and then re-insert them at the new location.
  • Use a virtual list view and move them in your data structure.

My routine for doing the first of these options is like this:

procedure TBatchTaskList.MoveTasks(const Source: array of TListItem; Target: TListItem);
var
  i, InsertIndex: Integer;
begin
  Assert(IsMainThread);
  BeginUpdate;
  Try
    //work out where to move them
    if Assigned(Target) then begin
      InsertIndex := FListItems.IndexOf(Target);
    end else begin
      InsertIndex := FListItems.Count;
    end;

    //create new items for each moved task
    for i := 0 to high(Source) do begin
      SetListItemValues(
        FListItems.Insert(InsertIndex+i),
        TBatchTask(Source[i].Data)
      );
      Source[i].Data := nil;//handover ownership to the new item
    end;

    //set selection and focus item to give feedback about the move
    for i := 0 to high(Source) do begin
      FListItems[InsertIndex+i].Selected := Source[i].Selected;
    end;
    FBatchList.ItemFocused := FListItems[InsertIndex];

    //delete the duplicate source tasks
    for i := 0 to high(Source) do begin
      Source[i].Delete;
    end;
  Finally
    EndUpdate;
  End;
end;

The method SetListItemValues is used to populate the columns of the list view.

This is a perfect example of why virtual controls are so great.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    `Source` contains the items to be moved. They are moved to appear before `Target`. You'll need to re-work it to fit into your code, the point of showing you the code is to express the idea rather than give you something you can use "as-is". However, if you could do a virtual list view then that would be better. – David Heffernan Mar 13 '11 at 14:03