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

时间:2016-09-23 17:12:36

标签: delphi statusbar

我正在尝试在TStatusPanel事件中获取TStatusBarOnDblClick的面板)的索引,以便将其与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');

4 个答案:

答案 0 :(得分:5)

您可以在GetMessagePos事件处理程序中使用OnDblClick来获取检索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;

答案 1 :(得分: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 :(得分:2)

几乎相同的逻辑,但它从StatusBar获得实际的面板边界。换句话说,如果单击面板之间的分隔符,它将返回-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;

答案 3 :(得分:0)

我使用了此解决方案,并且对我有用。简单明了!

首先,为StatusBar的OnMouseDown事件编写一个处理程序

procedure TfrmMain.StatusBarMouseDown(Sender: TObject; Button: TMouseButton; Shift:
  TShiftState; X, Y: Integer);
begin
  TComponent(Sender).Tag := X;
end;

然后为StatusBar的OnDoublClick事件编写一个处理程序

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;