【问题标题】:DataGridView Column drag and drop with Auto-horizontal scrollDataGridView 列拖放与自动水平滚动
【发布时间】:2010-05-20 21:04:57
【问题描述】:

谁能建议我应该如何在 DataGridView 中实现列拖放(自动滚动)功能。我知道我可以使用控件的 AllowUserToDragDrop 选项。但是,由于我的 datagridview 控件的列数相对较多,因此我需要一个跟随当前拖放位置的自动滚动功能,以便用户在拖放之前可以看到目标列。我已经实现了自定义拖放功能,但我仍然无法启用自动滚动选项。

【问题讨论】:

    标签: datagridview drag-and-drop scroll custom-scrolling


    【解决方案1】:

    我正在使用以下类来自动滚动 TTreeView。 TScroller 是在它所在的 Frame 的 Create 中创建的,传入 TreeView。它在框架的 Destroy 中被销毁。在 TreeView 的 OnDragOver 中,我只需调用 MyDragScroller.Scroll(State);

    type
      TScroller = class(TObject)
      private
        MyTimer: TTimer;
        FControl: TWinControl;
        FSensitiveSize: Integer;
      protected
        procedure HandleTimer(Sender: TObject);
      public
        constructor Create(aControl: TWinControl);
        destructor Destroy; override;
    
        procedure Scroll(const aState: TDragState);
      end;
    
    implementation
    
    { TScroller }
    
    constructor TScroller.Create(aControl: TWinControl);
    begin
      inherited Create;
      MyTimer := TTimer.Create(nil);
      MyTimer.Enabled := False;
      MyTimer.Interval := 20; // Not too short, otherwise scrolling flashes by.
      MyTimer.OnTimer := HandleTimer;
    
      FControl := aControl;
      // Width/Height from edge of FControl within which the mouse has to be for
      // automatic scrolling to occur. By default it is the width of a vertical scrollbar.
      FSensitiveSize := GetSystemMetrics(SM_CXVSCROLL);
    end;
    
    destructor TScroller.Destroy;
    begin
      FreeAndNil(MyTimer);
      FControl := nil;
      inherited;
    end;
    
    procedure TScroller.HandleTimer(Sender: TObject);
    var
      MousePos: TPoint;
      MouseX: Integer;
      MouseY: Integer;
    
      function _MouseInSensitiveSize: Boolean;
      begin
    
        MousePos := FControl.ScreenToClient(Mouse.CursorPos);
        MouseY := MousePos.Y;
        MouseX := MousePos.X;
    
        Result :=
             ((MouseY >= 0) and (MouseY < FSensitiveSize))
          or ((MouseY > FControl.ClientHeight - FSensitiveSize) and (MouseY <= FControl.ClientHeight))
          or ((MouseX >= 0) and (MouseX < FSensitiveSize))
          or ((MouseX > FControl.ClientWidth - FSensitiveSize) and (MouseX <= FControl.ClientWidth))
        ;
    
      end;
    begin
      if Mouse.IsDragging and _MouseInSensitiveSize then begin
        if MouseY < FSensitiveSize then begin
          FControl.Perform(WM_VSCROLL, SB_LINEUP, 0);
        end else if MouseY > FControl.ClientHeight - FSensitiveSize then begin
          FControl.Perform(WM_VSCROLL, SB_LINEDOWN, 0);
        end;
    
        if MouseX < FSensitiveSize then begin
          FControl.Perform(WM_HSCROLL, SB_LINELEFT, 0);
        end else if MouseX > FControl.ClientWidth - FSensitiveSize then begin
          FControl.Perform(WM_HSCROLL, SB_LINERIGHT, 0);
        end;
      end else begin
        MyTimer.Enabled := False;
      end;
    end;
    
    procedure TScroller.Scroll(const aState: TDragState);
    begin
      if not Mouse.IsDragging then Exit;  // Only scroll while dragging.
      if not (aState in [dsDragMove]) then Exit; // No use scrolling on a dsDragLeave and not nice to do so on a dsDragEnter.
    
      MyTimer.Enabled := True;
    end;
    

    注意事项: 如果您有更多需要自动滚动的控件,则需要为每个控件创建一个 TScroller。在这种情况下,使用某种观察者/被观察机制在所有滚动控件之间共享计时器可能会对您的应用程序的性能产生很大的好处。

    【讨论】:

      【解决方案2】:

      您可以处理 OnMouseMove,并以编程方式进行相应的滚动。

      【讨论】:

      • 谢谢,我试过了(实际上我试过 OnMouseMove 和 OnDragDrop 事件)。它工作正常,只是它不能灵活地向后和向前滚动。您建议使用哪种水平滚动条方法来分配当前鼠标光标位置?
      • 您查看过 FirstDisplayedScrollingRowIndex 属性吗?
      • 是的,我在上述指定事件上使用了 FirstDisplayedColumnIndex。虽然自动滚动是这样工作的,但它对用户来说并不那么方便。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-06
      相关资源
      最近更新 更多