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