Search code examples
delphidelphi-7trackbar

TTrackBar with custom positions?


I am trying to duplicate the behaviour of PAINT application in Win 7 zoom track bar: (I know it's a common track bar control)

100% Zoom

The 100% is located in the center. and it has 11 available positions:

50% Zoom 200% Zoom etc...

12.5%, 25%, 50%, 100%, 200%, 300%, 400%, 500%, 600%, 700%, 800%

So my zoom values (ZoomArray) are:
0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6, 7, 8

That's easy I could set Min to 1 and Max to 11 and get the values I need:
ZoomArray[TrackBar1.Position]

The question is how to keep 100% in the center and the only positions that are available are the one above?

I have tried to use dummy values in the array to keep the 1 in the center e.g.:
0.125, 0.25, 0.5, -1, -1, -1, -1, 1, 2, 3, 4, 5, 6, 7, 8
And reposition the trackbar on Change event, but my logic doesnt seem to work right.

Any ideas?


Solution

  • Here is one alternative that derives a new control from TTrackbar, removing the automatic tics and handling sliding in the scroll message, behaves nearly identical to the control in Paint. Compiled with D2007, tried to comment a little:

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Controls, Forms, ComCtrls, StdCtrls;
    
    type
      TCNHScroll = TWMHScroll;
    
      TTrackBar = class(comctrls.TTrackBar)  // interposer class for quick test
      protected
        procedure CreateParams(var Params: TCreateParams); override;
        procedure CreateWnd; override;
        procedure CNHScroll(var Message: TCNHScroll); message CN_HSCROLL;
      public
        constructor Create(AOwner: TComponent); override;
      end;
    
      TForm1 = class(TForm)
        Label1: TLabel;
        TrackBar1: TTrackBar;
        procedure TrackBar1Change(Sender: TObject);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses
      commctrl;
    
    {$R *.dfm}
    
    procedure TForm1.TrackBar1Change(Sender: TObject);
    begin
      // account for non-linear scaling for a sensible value
      if TrackBar1.Position <= 8 then
        Label1.Caption := IntToStr(TrackBar1.Position * 125)
      else
        Label1.Caption := IntToStr(TrackBar1.Position * 1000 - 7000)
    end;
    
    { TTrackBar }
    
    constructor TTrackBar.Create(AOwner: TComponent);
    begin
      inherited;
    
      // We'll have 15 positions which should account for the following values 
      // 125 250 - 500 - - - 1000 2000 3000 4000 5000 6000 7000 8000
      // positions 3, 5..7 will be skipped when tracking
      Min := 1;
      Max := 15;
      LineSize := 1;
      PageSize := 1;
    end;
    
    procedure TTrackBar.CreateParams(var Params: TCreateParams);
    begin
      inherited;
      // remove automatic ticks so that we don't have ticks at 3 and 5..7
      Params.Style := Params.Style and not TBS_AUTOTICKS;
    end;
    
    procedure TTrackBar.CreateWnd;
    begin
      inherited;
      // first and last tick not required
      SetTick(2);  //  250
      SetTick(4);  //  500
      SetTick(8);  // 1000
      SetTick(9);  // 2000
      SetTick(10); 
      SetTick(11);
      SetTick(12);
      SetTick(13);
      SetTick(14); // 7000
    end;
    
    procedure TTrackBar.CNHscroll(var Message: TCNHScroll);
    var
      Pos: Integer;
    begin
      // prevent jumping back and forth while thumb tracking, do not slide to the
      // next tick until a threshold is passed
      if Message.ScrollCode = SB_THUMBTRACK then begin
        case Message.Pos of            
          5: SendMessage(Handle, TBM_SETPOS, 1, 4);
          6, 7: SendMessage(Handle, TBM_SETPOS, 1, 8);
        end;
      end;
    
      // for line and page and rest of the scrolling, skip certain ticks
      Pos := SendMessage(Handle, TBM_GETPOS, 0, 0);
      if Pos > Position then      // compare with previous position
        case Pos of
          3: SendMessage(Handle, TBM_SETPOS, 1, 4);
          5..7: SendMessage(Handle, TBM_SETPOS, 1, 8);
        end;
      if Pos < Position then
        case Pos of
          3: SendMessage(Handle, TBM_SETPOS, 1, 2);
          5..7: SendMessage(Handle, TBM_SETPOS, 1, 4);
        end;
    
      inherited;
    end;
    
    end.
    

    Vertical implementation would be similar, if needed. This is not really a finished product, just a trial to mimic the behavior of the mentioned control.