본문 바로가기

프로그래밍/Delphi

[Delphi/Tip] TListView에서 ListItem 순서바꾸기/이동

[Delphi/Tip] TListView에서 ListItem 순서바꾸기/이동

[개발 실전에서 바로 활용 가능한 라이브 코드 만들기]


이번에는 기본 리스트뷰 컴포넌트인 TListView에서 등록된 아이템(TListItem)들간의 순서를 바꾸거나 이동 하는 방법에 대해 알아 보자.



이 프로젝트를 통해서 객체와 그 객체를 목록으로 가지고, 객체 목록을 리스트뷰와 연동하여 관리하는 방법을 덤으로 배울 수 있다. 


또한 리스트아이템(ListItem) 또는 노드(TTreeView에서의 표현)간에 버튼을 이용하거나 드래그 드롭(Drag&Drop)을 이용해 이동할 때 궁극적으로 재사용이 가능한 위치 이동 함수를 만들어 보자


우리가 아마도 두개의 방을 바꾼는 알고리즘을 배웠을 것이다. 두개의 방을 바꾸기 위한 임시 방을 만드는 방법이 여기에 적용 된다.


리스트아이템(TListItem)간의 이동을 위한 중요한 핵심 코드는 다음과 같다.


//TListView에서 순서를 가진 두개의 TListItem의 위치를 교환 한다.
procedure ExchangeListItems(ListView : TListView; const Idx1, Idx2: Integer);
var
  tempLI: TListItem;
begin
  ListView.Items.BeginUpdate;
  try
    tempLI := TListItem.Create(ListView.Items);
    tempLI.Assign(ListView.Items.Item[Idx1]);
    ListView.Items.Item[Idx1].Assign(ListView.Items.Item[Idx2]);
    ListView.Items.Item[Idx2].Assign(tempLI);
    ListView.Selected := ListView.Items[Idx2];
    tempLI.Free;
  finally
    ListView.Items.EndUpdate;
  end;
end;

// 실 사용 방법은 다음과 같다.
    ExchangeListItems(ListView1, 1, 2); //TListView에서 두번째 아이템과 세번째 아이템을 서로 바꾼다


프로젝트 전체의 소스는 다음과 같으니 아이템간의 바꾸기 외에도 리스트뷰와 객체를 어떻게 효율적으로 연동해서 사용하는지 배울 수 있으니 코드를 살펴 보기를 권장한다.


unit uDemoExchangeListItem;
{**
  리스튜뷰의 아이템(TListItem)의 순서를 변경할 수 있는 샘플 프로젝트
  Author : Dukwoo Lee(niceondal@gmail.com)
  Date : 2018-11-01
  주요기능 :
    - 객체를 리스트로 관리하고 리스트뷰에 객체 목록을 연동하는 매커니즘을 이해한다.
    - 리스트뷰의 노드(아이템)의 순서를 바꿀 수 있다.
  *}

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, contnrs, Vcl.ComCtrls, Vcl.StdCtrls;

type
  //데모 객체 클래스
  TSite = class
  private
    //사이트명
    FSiteName : String;
    //URL
    FURL : String;
    //대표자
    FCeo : String;
    //주소
    FAddress : String;
  end;

  //객체 리스트 클래스
  //리스트가 해제 될 때 리스트에 담긴 객체도 자동 해제처리 하기 위해 TObjectList를 사용했으며
  //TObjectList를 사용하려면 Uses절에 "contnrs" 유닛을 포함해 주어야 한다.
  //TList를 상속 받는다면 리스트에 포함된 모든 객체는 따로 해제해 주는 부분이 있어야 한다.
  TSiteList = class(TObjectList)
  private
    function _GetSite(nIdx: Integer): TSite;
    property Items[nIdx : Integer] : TSite read _GetSite;
  end;

  TfrmExchangeListItem = class(TForm)
    lvListView: TListView;
    btnUp: TButton;
    btnDown: TButton;
    procedure btnUpClick(Sender: TObject);
    procedure btnDownClick(Sender: TObject);
    procedure lvListViewCustomDrawItem(Sender: TCustomListView; Item: TListItem;
      State: TCustomDrawState; var DefaultDraw: Boolean);
  private
    FSiteList: TSiteList;
    { Private declarations }
    //데모를 위한 샘플 데이터를 생성한다.
    procedure InitSampleData;
    //ListItem에 지정된 객체를 표시한다.
    procedure DrawListItem(Item : TListItem; ASite : TSite);
    //리스트 객체를 TListView에 적용한다.
    procedure DrawItems;
  public
    { Public declarations }
    property SiteList : TSiteList read FSiteList write FSiteList;

    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  end;

var
  frmExchangeListItem: TfrmExchangeListItem;

implementation

{$R *.dfm}

//TListView에서 순서를 가진 두개의 TListItem의 위치를 교환 한다.
procedure ExchangeListItems(ListView : TListView; const Idx1, Idx2: Integer);
var
  tempLI: TListItem;
begin
  ListView.Items.BeginUpdate;
  try
    tempLI := TListItem.Create(ListView.Items);
    tempLI.Assign(ListView.Items.Item[Idx1]);
    ListView.Items.Item[Idx1].Assign(ListView.Items.Item[Idx2]);
    ListView.Items.Item[Idx2].Assign(tempLI);
    ListView.Selected := ListView.Items[Idx2];
    tempLI.Free;
  finally
    ListView.Items.EndUpdate;
  end;
end;

{ TfrmExchangeListItem }

procedure TfrmExchangeListItem.btnDownClick(Sender: TObject);
begin
  //선택된 아이템이 있고 첫번째 아이템이 마지막이 아닌경우만 실행
  if (lvListView.Selected<>nil) and (lvListView.Selected.Index<lvListView.Items.Count-1) then
    ExchangeListItems(lvListView, lvListView.Selected.Index, lvListView.Selected.Index+1);
end;

procedure TfrmExchangeListItem.btnUpClick(Sender: TObject);
begin
  //선택된 아이템이 있고 첫번째 아이템이 아닌 경우만 실행
  if (lvListView.Selected<>nil) and (lvListView.Selected.Index>0) then
    ExchangeListItems(lvListView, lvListView.Selected.Index, lvListView.Selected.Index-1);
end;

constructor TfrmExchangeListItem.Create(AOwner: TComponent);
begin
  inherited;

  //리스트객체 생성
  FSiteList := TSiteList.Create;
  InitSampleData;
  DrawItems;
end;

destructor TfrmExchangeListItem.Destroy;
begin
  //리스트객체 해제
  FSiteList.Free;

  inherited;
end;

//SiteList 리스트 객체를 TListView에 할당
procedure TfrmExchangeListItem.DrawItems;
var
  i : integer;
  lt : TListItem;
  siteObj : TSite;
begin
  lvListView.Items.Clear;
  for i := 0 to SiteList.Count-1 do
  begin
    siteObj := SiteList.Items[i];
    lt := lvListView.Items.Add;
    DrawListItem(lt, siteObj);
  end;
end;

//하나의 Site객체를 리스트아이템(ListItem)에 할당
procedure TfrmExchangeListItem.DrawListItem(Item: TListItem; ASite : TSite);
begin
  if Item=nil then
    exit;

  if Item.SubItems.Count=0 then
  begin
    Item.SubItems.Add('');
    Item.SubItems.Add('');
    Item.SubItems.Add('');
    Item.SubItems.Add('');
  end;

  Item.SubItems[0] := ASite.FSiteName;
  Item.SubItems[1] := ASite.FURL;
  Item.SubItems[2] := ASite.FCeo;
  Item.SubItems[3] := ASite.FAddress;
  Item.Data := ASite;
end;

//데모를 위한 초기 샘플 데이터 생성
procedure TfrmExchangeListItem.InitSampleData;
var
  site : TSite;
begin
  SiteList.Clear;

  site := TSite.Create;
  site.FSiteName := '네이버';
  site.FUrl := 'http://www.naver.com';
  site.FCeo := '한성숙';
  site.FAddress := '경기도 성남시 분당구 불정로 6 NAVER그린팩토리';
  SiteList.Add(site);

  site := TSite.Create;
  site.FSiteName := '다음';
  site.FUrl := 'http://www.daum.net';
  site.FCeo := '여민수';
  site.FAddress := '제주특별자치도 제주시 첨단로 242 ';
  SiteList.Add(site);

  site := TSite.Create;
  site.FSiteName := '조선일보';
  site.FUrl := 'http://www.chosun.com/';
  site.FCeo := '방우영';
  site.FAddress := '서울특별시 중구 세종대로21길 30';
  SiteList.Add(site);

  site := TSite.Create;
  site.FSiteName := '경향신문';
  site.FUrl := 'http://www.khan.co.kr';
  site.FCeo := '이동현';
  site.FAddress := '서울시 중구 정동길 3 경향신문사';
  SiteList.Add(site);

  site := TSite.Create;
  site.FSiteName := '한겨레신문';
  site.FUrl := 'http://www.hani.co.kr/';
  site.FCeo := '양상우';
  site.FAddress := '서울특별시 마포구 효창목길 6';
  SiteList.Add(site);
end;

procedure TfrmExchangeListItem.lvListViewCustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  if Item=nil then
    exit;

  Item.Caption := IntToStr(Item.Index+1);
end;

{ TSiteList }

function TSiteList._GetSite(nIdx: Integer): TSite;
begin
  result := TSite(Get(nIdx));
end;

end.


도움이 되셨다면 광고 한 번만 클릭 매너^^


프로젝트 소스 다운로드 :  demoExchangeListItem.zip