2016-09-23 3 views
2

Я пытаюсь получить индекс TStatusPanel (панель TStatusBar) в событии OnDblClick, чтобы использовать его с ShowMessage(), например, но я не знаю, как получить индекс.Как получить индекс TStatusPanel (StatusBar - Delphi)?

Я знаю, что такие события, как 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

Вызвать GetMessagePos и ​​выяснить, на какую панель нажали –

+0

Дэвид Хеффернан, спасибо за ответ, но StatusBar находится в изменяемой форме, GetMessagePos возвращает разные позиции. – SisMaster

+0

Так что. Вы должны знать, где находятся панели. И выясните, какой из них вы используете. –

ответ

4

Вы можете использовать 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

Почти такая же логика, но она получает фактические границы панели из 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; 
+2

Вы были более точными с SB_RECT, но затем отказались от точности с GetCursorPos. –

+0

Очень важно использовать GetMessagePos –

+0

Спасибо, исправлено для GetMessagePos –

2

Я предложил бы использовать 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; 

Alternativly, если вы используете 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;