項目のD&D移動時の移動位置表示 (TabControl/PageControl)

今回は TabControl/PageControl のD&Dでのタブ移動の処理です。

余りよろしくない方法な気もしますが。まずは、TabControlでもPageControlでも同じイベント処理が使えるように、またPageControlでタブの移動が出来るようにTCustomTabControlに対するヘルパークラスを作成。これでTCustomTabControlのTabsとTabIndexに対するアクセスを確保します。

  TTabsAccessHelper = class helper for TCustomTabControl
  private
    function GetActiveTabIndex: Integer;
    function GetTabItems: TStrings;
    procedure SetActiveTabIndex(const Value: Integer);
    procedure SetTabItems(const Value: TStrings);
  public
    property ActiveTabIndex: Integer read GetActiveTabIndex write SetActiveTabIndex;
    property TabItems: TStrings read GetTabItems write SetTabItems;
  end;

{ TTabsAccessHelper }

function TTabsAccessHelper.GetActiveTabIndex: Integer;
begin
  Result := TabIndex;
end;

function TTabsAccessHelper.GetTabItems: TStrings;
begin
  Result := Tabs;
end;

procedure TTabsAccessHelper.SetActiveTabIndex(const Value: Integer);
begin
  TabIndex := Value;
end;

procedure TTabsAccessHelper.SetTabItems(const Value: TStrings);
begin
  Tabs := Value;
end;

前回と同じく、OnStartDrag、OnDragOver、OnDragDrop、OnMouseDownを実装します。
FPrevIdxはフォーム変数です。

procedure TForm1.StartDrag(Sender: TObject; var DragObject:
    TDragObject);
begin
  FPrevIdx := -1;
end;

procedure TForm1.MouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    (Sender As TControl).BeginDrag(False, 5);
end;

procedure TForm1.PageControl1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  CurIndex: Integer;
  NewIndex: Integer;

  TabControl: TTabControl;
  PageControl: TPageControl;
begin
  if Sender is TPageControl then
  begin
    PageControl := Sender as TPageControl;
    NewIndex := PageControl.IndexOfTabAt(X,Y);
    CurIndex := PageControl.ActivePageIndex;
    PageControl.Pages[CurIndex].PageIndex := NewIndex;
  end else if Sender is TTabControl then
  begin
    TabControl := Sender as TTabControl;
    NewIndex := TabControl.IndexOfTabAt(X,Y);
    CurIndex := TabControl.ActiveTabIndex;

    TabControl.Tabs.Move(CurIndex, NewIndex);
    TabControl.TabIndex := NewIndex;
  end;

  FPrevIdx := -1;
end;

procedure TForm1.PageControl1DragOver(Sender, Source: TObject; X, Y: Integer;
    State: TDragState; var Accept: Boolean);
var
  TargetIdx: Integer;
  CurIndex: Integer;
  tabRect: TRect;

  TabControl: TCustomTabControl;
begin
  TabControl := Sender as TCustomTabControl;
  TargetIdx := TabControl.IndexOfTabAt(X, Y);
  CurIndex := TabControl.ActiveTabIndex;
  // 移動先と現在のインデックスが同じ場合はドロップ不可
  Accept := (Sender = Source) And (TargetIdx <> CurIndex);

  TabControl.Canvas.Pen.Color := clRed;
  TabControl.Canvas.Pen.Mode := pmXor;
  TabControl.Canvas.Pen.Style := psSolid;
  TabControl.Canvas.Pen.Width := 5;

  if Accept then
  begin
    if FPrevIdx > -1 then
    begin
      tabRect := TabControl.TabRect(FPrevIdx);

      if FPrevIdx > CurIndex then
      begin
        TabControl.Canvas.MoveTo(tabRect.Right, tabRect.Top);
        TabControl.Canvas.LineTo(tabRect.Right, tabRect.Bottom);
      end else begin
        TabControl.Canvas.MoveTo(tabRect.Left, tabRect.Top);
        TabControl.Canvas.LineTo(tabRect.Left, tabRect.Bottom);
      end;
    end;

    if TargetIdx > -1 then
    begin
      tabRect := TabControl.TabRect(TargetIdx);

      if (TargetIdx >= TabControl.ActiveTabIndex) then
      begin
        TabControl.Canvas.MoveTo(tabRect.Right, tabRect.Top);
        TabControl.Canvas.LineTo(tabRect.Right, tabRect.Bottom);
      end else begin
        TabControl.Canvas.MoveTo(tabRect.Left, tabRect.Top);
        TabControl.Canvas.LineTo(tabRect.Left, tabRect.Bottom);
      end;
      FPrevIdx := TargetIdx;
    end;
  end;
end;


追記

PageControlは、TabSheetの方がついてきませんね。PageControlの方は使えません。orz

追記2

OnDragDropを修正しました。TTabsAccessHelperのTabItemsは不要になりました。PageControlの移動も大丈夫になりました。