如何获取TStatusPanel(StatusBar - Delphi)的索引?

4
我正在尝试在OnDblClick事件中获取TStatusBarTStatusPanel(面板)的索引,以便例如使用ShowMessage(),但我不知道如何获取索引。
我知道像OnDrawPanel这样的事件有一个Panel:TStatusPanel参数,但我需要同样的东西在OnDblClick中,但只有一个参数Sender:TObject
没有像if StatusBar.Panel = 1这样的命令。 我可以使用StatusBar.Panels [0],但我不知道如何比较单击的索引以显示我的消息。
好吧,这就是我需要的简单方法:
if StatusBar.Panel = 0 then
  showmessage('0')
else if StatusBar.Panel = 1 then
  showmessage('1');

我知道上面的代码不起作用,那只是一个例子。应该像这样:
if StatusBar.Panels[0].'SOMETHING' = 0 then
  showmessage('0')
else if StatusBar.Panels[0].'SOMETHING' = 1 then
  showmessage('1');

调用 GetMessagePos 函数并计算出单击了哪个面板。 - David Heffernan
David Heffernan,感谢您的回答,但是StatusBar在可调整大小的窗体上,GetMessagePos返回不同的位置。 - SisMaster
4个回答

8

您可以在OnDblClick事件处理程序中使用GetMessagePos来获取鼠标位置,当检索到触发双击处理程序的WM_LBUTTONDBLCLK消息并将其转换为客户端坐标时。然后,您可以遍历状态栏的面板以定位鼠标所在的部分。示例:

procedure TForm1.StatusBar1DblClick(Sender: TObject);
var
  Pt: TPoint;
  i, w: Integer;
begin
  Pt := SmallPointToPoint(TSmallPoint(DWORD(GetMessagePos)));
  MapWindowPoints(HWND_DESKTOP, StatusBar1.Handle, Pt, 1);
  w := 0;
  for i := 0 to StatusBar1.Panels.Count - 1 do begin
    w := w + StatusBar1.Panels[i].Width;
    if Pt.X < w then begin
      ShowMessage(IntToStr(i));
      Break;
    end;
  end;
end;

你可以选择使用OnMouseDown事件处理程序,该处理程序已经传递了鼠标单击的客户端坐标,并在事件处理程序中测试双击,然后定位面板。使用OnMouseDown处理程序没有任何副作用,因为它是从同一个WM_LBUTTONDBLCLK触发的双击事件。

procedure TForm1.StatusBar1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  i, w: Integer;
begin
  if (Button = mbLeft) and (ssDouble in Shift) then begin
    w := 0;
    for i := 0 to StatusBar1.Panels.Count - 1 do begin
      w := w + StatusBar1.Panels[i].Width;
      if X < w then begin
        ShowMessage(IntToStr(i));
        Break;
      end;
    end;
  end;
end;

已使用并在我的“Glitch”桌面终端中完美运作...谢谢! - Ozz Nixon

3

我建议使用SB_GETPARTS代替SB_GETRECT。这样,您发送给TStatusBar的消息会更少:

uses
  ..., Winapi.CommCtrl;

function GetStatusPanelAt(StatusBar: TStatusBar; X, Y: Integer): TStatusPanel; overload;
function GetStatusPanelAt(StatusBar: TStatusBar; const P: TPoint): TStatusPanel; overload;

...

function GetStatusPanelAt(StatusBar: TStatusBar; X, Y: Integer): TStatusPanel;
begin
  Result := GetStatusPanelAt(StatusBar, Point(X, Y));
end;

function GetStatusPanelAt(StatusBar: TStatusBar; const P: TPoint): TStatusPanel;
var
  index: Integer;
  arr: array of Integer;
  Panel: TStatusPanel;
begin
  Result := nil;

  if not PtInRect(StatusBar.ClientRect, P) then
    Exit;

  SetLength(arr, SendMessage(StatusBar.Handle, SB_GETPARTS, 0, 0));
  SendMessage(StatusBar.Handle, SB_GETPARTS, Length(arr), LPARAM(PInteger(arr)));

  index := 0;
  while index < Length(arr) do
  begin
    if (P.X <= arr[index]) or (arr[index] = -1) then
    begin
      Result := StatusBar.Panels[index];
      Exit;
    end;
    Inc(index);
  end;
end;

然后,您可以这样做:
uses
  ..., System.Types, Winapi.Windows;

procedure TForm5.StatusBar1DblClick(Sender: TObject);
var
  Pt: TPoint;
  Panel: TStatusPanel;
begin
  Pt := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  Pt := StatusBar1.ScreenToClient(Pt);

  Panel := GetStatusPanelAt(StatusBar1, Pt);
  if Panel <> nil then
    ShowMessage('Click on Panel ' + IntToStr(Panel.Index))
  else
    ShowMessage('No click on a Panel');
end;

或者,如果您使用的是D2006或更高版本,则可以将逻辑封装到类助手中:

uses
  ..., Winapi.CommCtrl;

type
  TStatusBarHelper = class helper for TStatusBar
  public
    function GetPanelAt(X, Y: Integer): TStatusPanel; overload;
    function GetPanelAt(const P: TPoint): TStatusPanel; overload;
  end;

...

function TStatusBarHelper.GetPanelAt(X, Y: Integer): TStatusPanel;
begin
  Result := GetPanelAt(Point(X, Y));
end;

function TStatusBarHelper.GetPanelAt(const P: TPoint): TStatusPanel;
var
  index: Integer;
  arr: array of Integer;
  Panel: TStatusPanel;
begin
  Result := nil;

  if not PtInRect(Self.ClientRect, P) then
    Exit;

  SetLength(arr, SendMessage(Self.Handle, SB_GETPARTS, 0, 0));
  SendMessage(Self.Handle, SB_GETPARTS, Length(arr), LPARAM(PInteger(arr)));

  index := 0;
  while index < Length(arr) do
  begin
    if (P.X <= arr[index]) or (arr[index] = -1) then
    begin
      Result := Self.Panels[index];
      Exit;
    end;
    Inc(index);
  end;
end;

uses
  ..., System.Types, Winapi.Windows;

procedure TForm5.StatusBar1DblClick(Sender: TObject);
var
  Pt: TPoint;
  Panel: TStatusPanel;
begin
  Pt := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  Pt := StatusBar1.ScreenToClient(Pt);

  Panel := StatusBar1.GetPanelAt(Pt);
  if Panel <> nil then
    ShowMessage('Click on Panel ' + IntToStr(Panel.Index))
  else
    ShowMessage('No click on a Panel');
end;

2
几乎相同的逻辑,但它从状态栏获取实际面板边界。换句话说,如果您单击面板之间的分隔符,它将返回-1。
uses 
  Winapi.CommCtrl;

procedure TForm1.StatusBar1DblClick(Sender: TObject);
var
  LClickPos: TPoint;
  LIndex: Integer;

  function GetPanelIndex: Integer;
  var
    I: Integer;
    LRect: TRect;
  begin
    for I := 0 to StatusBar1.Panels.Count - 1 do
    begin
      if SendMessage(StatusBar1.Handle, SB_GETRECT, I, LPARAM(@LRect)) <> 0 then
      begin
        if PtInRect(LRect, LClickPos) then
          Exit(I);
      end;
    end;

    Result := -1;
  end;

begin
  LClickPos := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  LClickPos := StatusBar1.ScreenToClient(LClickPos);
  LIndex := GetPanelIndex;

  Application.MessageBox(PChar(Format('Panel %d', [LIndex])), 'Test');
end;

2
你在使用SB_RECT时更加准确,但在使用GetCursorPos时放弃了准确性。 - Sertac Akyuz
使用 GetMessagePos 非常重要。 - David Heffernan
谢谢,已修复为GetMessagePos。 - Max Abramovich

1
我使用了这个解决方案,它对我很有效。简单明了!
首先,在 StatusBar 的 OnMouseDown 事件中编写一个处理程序。
procedure TfrmMain.StatusBarMouseDown(Sender: TObject; Button: TMouseButton; Shift:
  TShiftState; X, Y: Integer);
begin
  TComponent(Sender).Tag := X;
end;

然后编写一个处理程序来处理StatusBar的OnDoubleClick事件。
procedure TfrmMain.StatusBarDblClick(Sender: TObject);
var
  AccumelatedWidth, i : Integer;
begin
  AccumelatedWidth := 0;
  for i := 0 to StatusBar.Panels.Count - 1 do
    begin
      AccumelatedWidth := AccumelatedWidth + StatusBar.Panels[i].Width;
      if StatusBar.Tag < AccumelatedWidth then
        begin
          ShowMessage ('You clicked panel ' + i.ToString);
          Break;
        end;
    end;
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接