【问题标题】:TTrackBar with custom positions?具有自定义位置的 TTrackBar?
【发布时间】:2013-01-30 11:39:51
【问题描述】:

我正在尝试在 Win 7 缩放轨迹栏中复制 PAINT 应用程序的行为: (我知道这是一个常见的轨迹栏控件)

100% 位于中心。 它有 11 个可用位置:

等等……

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

所以我的缩放值 (ZoomArray) 是:
0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6, 7, 8

这很容易,我可以将 Min 设置为 1 并将 Max 设置为 11 并获得我需要的值:
ZoomArray[TrackBar1.Position]

问题是如何保持 100% 在中心,而唯一可用的位置是上面那个?

我尝试在数组中使用虚拟值来将 1 保持在中心,例如:
0.125, 0.25, 0.5, -1, -1, -1, -1, 1, 2, 3, 4, 5, 6, 7, 8
并在更改事件上重新定位轨迹栏,但我的逻辑似乎无法正常工作。

有什么想法吗?

【问题讨论】:

  • 我的解决方案是设置足够的缩放值,使 100% 位于中间。使用其他任何东西都会很糟糕。
  • @David,Paint 所做的很有意义,它允许更简单(也更有意义)缩放位图。低于 100% 您将获得 50、25 和 12.5;一个屏幕像素是基于原始的 (2x2)、(4x4) 或 (8x8) 区域进行渲染的。超过 100 的缩放级别从一百到一百:一个像素被缩放到 (2x2)、(3x3) ... (8x8)。当然这很可怕,但 33% 的缩放在像素绘画应用程序中可能看起来不太好。
  • @CosminPrund 这取决于使用的调整大小算法。一个好的重采样器可以在 2 的非幂次方中做得很好。

标签: delphi delphi-7 trackbar


【解决方案1】:

这是一种从TTrackbar 派生新控件的替代方法,它移除了自动抽动并处理滚动消息中的滑动,其行为几乎与 Paint 中的控件相同。用D2007编译,试着评论一下:

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.

如果需要,垂直实施将是类似的。这并不是真正的成品,只是模拟上述控件行为的试验。

【讨论】:

  • @Vlad - 不客气!只有一件事我后来才注意到:VCL 已经支持自定义刻度。将TickStyle:=tsManual; 放入构造函数中,将无需重写 CreateParams。如果你愿意,我可以编辑代码。
  • @SertacAkyuz,我在 CreateParams 中添加了and not TBS_ENABLESELRANGE,所以无论如何我都在使用它。
  • 如何通过 [-] 或 [+] 按钮增加或减少位置?我需要发送 CN_HSCROLL 吗?似乎设置 TrackBar.Position := TrackBar.Position + 1 没有按预期工作。
  • @Vlad - 我认为最简单的解决方案是改变密钥。例如。在 OnKeyDown 处理程序中:if Key = VK_ADD then Key := VK_RIGHT;
  • @Vlad - 滚动消息就可以了。或者有条件地设置位置(取决于当前位置)。或者因为我现在对钥匙很着迷:),发送一个关键信息:TrackBar1.Perform(WM_KEYDOWN, VK_RIGHT, 0);
【解决方案2】:

TrackBar.Max 设置为14,并实现OnChangeOnKeyDown 处理程序,以及一些用于放大和缩小的按钮OnClick 处理程序。此外,设置 TrackBar.PageSize = 4 以使 PageUp 和 PageDown 键正常工作。

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;

【讨论】:

  • 这几乎是完美的(但我遇到了同样的问题)。如果位置为 0 并且您单击最右侧它不会超过位置 3。也请尝试使用箭头键。它在位置 2 或 3 上堆叠。
  • 但现在 100% 不在中心。
  • 似乎 PageSize 在这里起主要作用。如果我将它设置为 1(就像在 PAINT 中一样)并尝试单击最正确的位置,它会再次获得堆栈。我认为 Sertac 的解决方案更优雅一些,因为它不涉及按键代码,工作原理和外观与 PAINT 完全一样。感谢您的努力和时间!
猜你喜欢
  • 2011-01-21
  • 2018-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-17
  • 2021-10-24
相关资源
最近更新 更多