2009. március 12., csütörtök

How to create a TPanel with scrollbars


Problem/Question/Abstract:

I want to create a component that has scrollbars (vertical/ horizontal). I tried to get the tricks from TCustomGrid but it doesn't work when I try to set a range/ position value to one of the scrollbars.

Answer:

This example uses an interposer class for convenience (mine, I just wanted to avoid the hassle of creating and installing a proper component for this example) but you should be able to adapt it for a proper component.

{ Example for fitting a panel with scrollbars }

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, ExtCtrls;

type
  TPanel = class(Extctrls.TPanel)
  private
    procedure WMVScroll(var msg: TWMSCROLL); message WM_VSCROLL;
    procedure WMHScroll(var msg: TWMSCROLL); message WM_HSCROLL;
    procedure WMGetDlgCode(var msg: TWMGetDlgCode); message WM_GETDLGCODE;
    procedure HandleScrollbar(var msg: TWMSCROLL; bar: Integer);
  protected
    procedure CreateParams(var params: TCreateParams); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TPanel }

procedure TPanel.CreateParams(var params: TCreateParams);
begin
  inherited;
  params.Style := params.Style or WS_VSCROLL or WS_HSCROLL;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  si: TScrollInfo;
begin
  si.cbSize := Sizeof(TScrollInfo);
  si.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
  si.nMin := 0;
  si.nMax := 3 * panel1.clientheight;
  si.nPage := panel1.clientheight div 2;
  si.nPos := 0;
  SetScrollInfo(panel1.handle, SB_VERT, si, true);
  si.nMax := 2 * panel1.clientwidth;
  si.nPage := panel1.clientwidth div 2;
  SetScrollInfo(panel1.handle, SB_HORZ, si, true);
end;

procedure TPanel.HandleScrollbar(var msg: TWMSCROLL; bar: Integer);
var
  si: TScrollInfo;
begin
  msg.result := 0;
  si.cbSize := Sizeof(TscrollInfo);
  si.fMask := SIF_ALL;
  GetScrollInfo(Handle, bar, si);
  si.fMask := SIF_POS;
  { For simplicities sake we use 1/10 of the page size as small scroll
         increment and the page size as large scroll increment }
  case msg.ScrollCode of
    SB_TOP: si.nPos := si.nMin;
    SB_BOTTOM: si.nPos := si.nMax;
    SB_LINEUP: Dec(si.nPos, si.nPage div 10);
    SB_LINEDOWN: Inc(si.nPos, si.nPage div 10);
    SB_PAGEUP: Dec(si.nPos, si.nPage);
    SB_PAGEDOWN: Inc(si.nPos, si.nPage);
    SB_THUMBTRACK, SB_THUMBPOSITION: si.nPos := msg.Pos;
    SB_ENDSCROLL: Exit;
  end;
  si.fMask := SIF_POS;
  if si.nPos < si.nMin then
    si.nPos := si.nMin;
  if si.nPos > si.nMax then
    si.nPos := si.nMax;
  SetScrollInfo(Handle, bar, si, true);
  { Fire a scroll notification off here to allow client to scroll content of panel }
end;

procedure TPanel.KeyDown(var Key: Word; Shift: TShiftState);

  procedure Scroll(scrollcode, message: Cardinal);
  begin
    Perform(message, scrollcode, 0);
  end;

const
  scrollkind: array[Boolean] of Cardinal = (WM_VSCROLL, WM_HSCROLL);
begin
  inherited;
  { Ignoring shift state for arrow keys here for simplicities sake }
  case Key of
    VK_UP: Scroll(SB_LINEUP, WM_VSCROLL);
    VK_LEFT: Scroll(SB_LINEUP, WM_HSCROLL);
    VK_DOWN: Scroll(SB_LINEDOWN, WM_VSCROLL);
    VK_RIGHT: Scroll(SB_LINEDOWN, WM_HSCROLL);
    VK_NEXT: Scroll(SB_PAGEDOWN, scrollkind[ssCtrl in Shift]);
    VK_PRIOR: Scroll(SB_PAGEUP, scrollkind[ssCtrl in Shift]);
    VK_HOME: Scroll(SB_TOP, scrollkind[ssCtrl in Shift]);
    VK_END: Scroll(SB_BOTTOM, scrollkind[ssCtrl in Shift]);
  end;
  Key := 0;
end;

procedure TPanel.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if (Button = mbLeft) and CanFocus and not Focused then
    SetFocus;
end;

procedure TPanel.WMGetDlgCode(var msg: TWMGetDlgCode);
begin
  msg.result := DLGC_WANTARROWS;
end;

procedure TPanel.WMHScroll(var msg: TWMSCROLL);
begin
  HandleScrollbar(msg, SB_HORZ);
end;

procedure TPanel.WMVScroll(var msg: TWMSCROLL);
begin
  HandleScrollbar(msg, SB_VERT);
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése