如何获取 TStatusPanel (StatusBar - Delphi) 的索引?
How to get index of a TStatusPanel (StatusBar - Delphi)?
我正在尝试在 OnDblClick
事件中获取 TStatusPanel
(TStatusBar
的面板)的索引以将其与 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');
您可以在 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;
几乎相同的逻辑,但它从 StatusBar 获取实际的面板边界。换句话说,如果您单击面板之间的分隔符,它将 return -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;
我建议使用 SB_GETPARTS
instead of 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 或更高版本,则可以将逻辑包装到 class 帮助程序中:
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;
我使用了这个解决方案并且它对我有用。简单明了!
首先,为 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;
我正在尝试在 OnDblClick
事件中获取 TStatusPanel
(TStatusBar
的面板)的索引以将其与 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');
您可以在 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;
几乎相同的逻辑,但它从 StatusBar 获取实际的面板边界。换句话说,如果您单击面板之间的分隔符,它将 return -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;
我建议使用 SB_GETPARTS
instead of 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 或更高版本,则可以将逻辑包装到 class 帮助程序中:
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;
我使用了这个解决方案并且它对我有用。简单明了!
首先,为 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;