質問

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?

役に立ちましたか?

解決

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.

他のヒント

Set TrackBar.Max to 14, and implement the OnChange and OnKeyDown handlers, as well as maybe some button OnClick handlers for zooming in and out. Also, set TrackBar.PageSize = 4 to get the PageUp and PageDown keys correctly working.

const
  ZoomTickCount = 15;

function ZoomLevelPos(Position: Integer; GoneUp: Boolean): Integer;
const
  Ticks: array[0..ZoomTickCount - 1] of Integer =
    (0, 1, 1, 2, 2, 2, 2, 9, 10, 11, 12, 13, 14, 15, 16);
begin
  Result := Position;
  if GoneUp then
    while (Result < High(Ticks)) and (Ticks[Result] = Ticks[Position - 1]) do
      Inc(Result)
  else
    while (Result > Low(Ticks)) and (Ticks[Result - 1] = Ticks[Position]) do
      Dec(Result);
end;

procedure TForm1.ZoomTrackBarChange(Sender: TObject);
const
  Zooms: array[0..ZoomTickCount - 1] of Single =
    (0.125, 0.25, 0.25, 0.5, 0.5, 0.5, 0.5, 1, 2, 3, 4, 5, 6, 7, 8);
begin
  ZoomTrackBar.Position := ZoomLevelPos(ZoomTrackBar.Position, False);
  Label1.Caption := Format('%.1n%%', [Zooms[ZoomTrackBar.Position] * 100]);
end;

procedure TForm1.ZoomTrackBarKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key in [VK_DOWN, VK_RIGHT] then
  begin
    ZoomTrackBar.Position := ZoomLevelPos(ZoomTrackBar.Position + 1, True);
    Key := 0;
  end;
end;

procedure TForm1.ZoomInButtonClick(Sender: TObject);
begin
  ZoomTrackBar.Position := ZoomLevelPos(ZoomTrackBar.Position + 1, True);
end;

procedure TForm1.ZoomOutButtonClick(Sender: TObject);
begin
  ZoomTrackBar.Position := ZoomTrackBar.Position - 1;
end;
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top