在边缘上方/附近拖动时滚动TTreeView

问题描述 投票:8回答:2

我有一个可以有很多节点的TTreeView,当扩展许多节点时,树使用了大量的屏幕空间。

现在,我想将TreeView底部附近的一个节点拖到顶部,由于我选择的节点在底部,因此我无法实际看到TreeView的顶部。当将节点拖动到TreeView的顶部时,我希望TreeView拖动时自动与我一起滚动,默认情况下似乎不会发生。

在Windows资源管理器中可以看到这种行为的完美示例。如果尝试拖动文件或文件夹,则将鼠标悬停在拖动的项目(节点)上时,它将根据光标位置自动向上或向下滚动。

希望如此。

PS,我已经知道如何拖动节点,如果希望将TreeView悬停在TreeView的顶部或底部附近,则我希望TreeView随我一起滚动。

谢谢。

delphi treeview scroll
2个回答
11
投票

这是我使用的代码。它适用于任何TWinControl后代:列表框,树视图,列表视图等。

type
  TAutoScrollTimer = class(TTimer)
  private
    FControl: TWinControl;
    FScrollCount: Integer;
    procedure InitialiseTimer;
    procedure Timer(Sender: TObject);
  public
    constructor Create(Control: TWinControl);
  end;

{ TAutoScrollTimer }

constructor TAutoScrollTimer.Create(Control: TWinControl);
begin
  inherited Create(Control);
  FControl := Control;
  InitialiseTimer;
end;

procedure TAutoScrollTimer.InitialiseTimer;
begin
  FScrollCount := 0;
  Interval := 250;
  Enabled := True;
  OnTimer := Timer;
end;

procedure TAutoScrollTimer.Timer(Sender: TObject);

  procedure DoScroll;
  var
    WindowEdgeTolerance: Integer;
    Pos: TPoint;
  begin
    WindowEdgeTolerance := Min(25, FControl.Height div 4);
    GetCursorPos(Pos);
    Pos := FControl.ScreenToClient(Pos);
    if not InRange(Pos.X, 0, FControl.Width) then begin
      exit;
    end;
    if Pos.Y<WindowEdgeTolerance then begin
      SendMessage(FControl.Handle, WM_VSCROLL, SB_LINEUP, 0);
    end else if Pos.Y>FControl.Height-WindowEdgeTolerance then begin
      SendMessage(FControl.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
    end else begin
      InitialiseTimer;
      exit;
    end;

    if FScrollCount<50 then begin
      inc(FScrollCount);
      if FScrollCount mod 5=0 then begin
        //speed up the scrolling by reducing the timer interval
        Interval := MulDiv(Interval, 3, 4);
      end;
    end;

    if Win32MajorVersion<6 then begin
      //in XP we need to clear up transient "fluff"; results in flickering so only do it in XP where it is needed
      FControl.Invalidate;
    end;
  end;

begin
  if Mouse.IsDragging then begin
    DoScroll;
  end else begin
    Free;
  end;
end;

然后要使用它,您需要为控件添加OnStartDrag事件处理程序并以如下方式实现它:

procedure TMyForm.SomeControlStartDrag(Sender: TObject; var DragObject: TDragObject);
begin
  TAutoScrollTimer.Create(Sender as TWinControl);
end;

1
投票

这里是基于所选节点始终自动在视图中滚动的事实的替代方法。

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    TreeView2: TTreeView;
    procedure TreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure TreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
    procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    FDragNode: TTreeNode;
    FNodeHeight: Integer;
  end;

...

procedure TForm1.TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  with TTreeView(Sender) do
  begin
    FDragNode := GetNodeAt(X, Y);
    if FDragNode <> nil then
    begin
      Selected := FDragNode;
      with FDragNode.DisplayRect(False) do
        FNodeHeight := Bottom - Top;
      BeginDrag(False, Mouse.DragThreshold);
    end;
  end;
end;

procedure TForm1.TreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var
  Pt: TPoint;
  DropNode: TTreeNode;
begin
  Accept := Source is TTreeView;
  if Accept then
    with TTreeView(Source) do
    begin
      if Sender <> Source then
        Pt := ScreenToClient(Mouse.CursorPos)
      else
        Pt := Point(X, Y);
      if Pt.Y < FNodeHeight then
        DropNode := Selected.GetPrevVisible
      else if Pt.Y > (ClientHeight - FNodeHeight) then
        DropNode := Selected.GetNextVisible
      else
        DropNode := GetNodeAt(Pt.X, Pt.Y);
      if DropNode <> nil then
        Selected := DropNode;
    end;
end;

procedure TForm1.TreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
var
  DropNode: TTreeNode;
begin
  with TTreeView(Sender) do
    if Target <> nil then
    begin
      DropNode := Selected;
      DropNode := Items.Insert(DropNode, '');
      DropNode.Assign(FDragNode);
      Selected := DropNode;
      Items.Delete(FDragNode);
    end
    else
      Selected := FDragNode;
end;

may也希望将OnDragOver事件处理程序链接到TreeView的父级,这将导致鼠标在TreeView之外时滚动和拖放。如果您要滚动而不是在鼠标移到TreeView之外时不放手,请在OnEndDrag事件处理程序中检查if Target = Sender

© www.soinside.com 2019 - 2024. All rights reserved.