XBasic3000
XBasic3000

Reputation: 3486

How can I disable the scroll-into-view behavior of TScrollBox?

I have a TScrollBox that has a RichEdit that is bigger than the scrollbox, so both side scrollbars appear in the scrollbox. Then I have a function DoTask that calls RichEdit.SetFocus.

When I scroll down to where I want to see part of the text control, and then call DoTask, the ScrollBox will automatically scroll to the top of the RichEdit. How can I avoid that?

Upvotes: 9

Views: 4682

Answers (4)

Harri Luoma
Harri Luoma

Reputation: 51

To disable scroll-into-view behavior from my main form, I used this solution: (C++Builder)

bool __fastcall TMainForm::SetFocusedControl(TWinControl *Control) {
    LockWindowUpdate(Handle);
    int vpos = VertScrollBar->Position;
    int hpos = HorzScrollBar->Position;
    bool result = TForm::SetFocusedControl(Control);
    if (VertScrollBar->Position != vpos) {
        VertScrollBar->Position = vpos;
    }
    if (HorzScrollBar->Position != hpos) {
        HorzScrollBar->Position = hpos;
    }
    LockWindowUpdate(0);
    return result;
}

Upvotes: 0

Fr0sT
Fr0sT

Reputation: 21

Without hacking into VCL/deriving custom components there's only one solution - TForm.SetFocusedControl override + re-setting the positions of scrollbars as said above. One thing I added is disabling/enabling window redraw to avoid ugly jumps. Here's my final snippet:

sbContainer is TScrollBox and NoScrCtrl is a control laying inside it which gets focus but we don't want it to be scrolled-in-view.

function TForm1.SetFocusedControl(Control: TWinControl): Boolean;
var hpos, vpos: integer;
begin
  if Control = NoScrCtrl then
  begin
    sbContainer.Perform(WM_SETREDRAW, WPARAM(False), 0);
    hpos := sbContainer.HorzScrollBar.Position;
    vpos := sbContainer.VertScrollBar.Position;
    Result := inherited SetFocusedControl(Control);
    sbContainer.HorzScrollBar.Position := hpos;
    sbContainer.VertScrollBar.Position := vpos;
    sbContainer.Perform(WM_SETREDRAW, WPARAM(True), 0);
    sbContainer.Refresh;
  end
  else
    Result := inherited SetFocusedControl(Control);
end;

Upvotes: 2

NGLN
NGLN

Reputation: 43664

As you wish, here are some suggestions:

  • Override SetFocusedControl in the form:

    function TForm1.SetFocusedControl(Control: TWinControl): Boolean;
    begin
      if Control = RichEdit then
        Result := True
      else
        Result := inherited SetFocusedControl(Control);
    end;
    

    Or:

    type
      TCustomMemoAccess = class(TCustomMemo);
    
    function TForm1.SetFocusedControl(Control: TWinControl): Boolean;
    var
      Memo: TCustomMemoAccess;
      Scroller: TScrollingWinControl;
      Pt: TPoint;
    begin
      Result := inherited SetFocusedControl(Control);
      if (Control is TCustomMemo) and (Control.Parent <> nil) and
        (Control.Parent is TScrollingWinControl) then
      begin
        Memo := TCustomMemoAccess(Control);
        Scroller := TScrollingWinControl(Memo.Parent);
        SendMessage(Memo.Handle, EM_POSFROMCHAR, Integer(@Pt), Memo.SelStart);
        Scroller.VertScrollBar.Position := Scroller.VertScrollBar.Position +
          Memo.Top + Pt.Y;
      end;
    end;
    
  • Interpose TScrollBox:

    type
      TScrollBox = class(Forms.TScrollBox)
      protected
        procedure AutoScrollInView(AControl: TControl); override;
      end;
    
    procedure TScrollBox.AutoScrollInView(AControl: TControl);
    begin
      if not (AControl is TCustomMemo) then
        inherited AutoScrollInView(AControl);
    end;
    

    Or:

    procedure TScrollBox.AutoScrollInView(AControl: TControl);
    begin
      if (AControl.Top > VertScrollBar.Position + ClientHeight) xor
          (AControl.Top + AControl.Height < VertScrollBar.Position) then
        inherited AutoScrollInView(AControl);
    end;
    

Or use any creative combination of all of the above. How and when you like it to be scrolled only you know.

Upvotes: 9

ertx
ertx

Reputation: 1544

the simpliest solution would be

var a, b : Integer;
begin
  a := ScrollBox1.VertScrollBar.Position;
  b := ScrollBox1.HorzScrollBar.Position;
  richEdit1.SetFocus;
  ScrollBox1.VertScrollBar.Position:=a ;
  ScrollBox1.HorzScrollBar.Position:=b ;
end;

Upvotes: 2

Related Questions