2014-01-23 2 views
3

Я использую Delphi 7, и у меня есть TFrame (размещенный TForm) с тремя панелями, которые охватывают всю поверхность, в макете «вверх тормашками». Панели должны быть изменены по размеру, поэтому я мог бы использовать 2 сплиттера, но я хочу дать лучший пользовательский интерфейс: я хотел бы иметь один «размерный захват» в T-соединении. Этот «дескриптор» должен появиться только тогда, когда пользователь навешивает область соединения.Delphi, Как показать наложенное управление движением мыши

Итак, вот мой вопрос: какой лучший способ иметь контрольное шоу над любым другим движением мыши? TFrame.OnMouseMove не вызываются (очевидно), потому что внутри есть панели внутри и, возможно, любые другие элементы управления внутри них. Я также очень хочу сохранить весь код внутри фрейма.

Я вижу 2 решения:

  1. установить локальный хук мыши и пойти с ним. Но могут возникнуть некоторые проблемы с производительностью (или нет?)
  2. Ручка TApplication.OnMessage внутри рамы, но при добавлении какой-либо другой код для того, чтобы имитировать «цепочку» обработчиков событий. Это связано с тем, что другим частям приложения может потребоваться обработать TApplication.OnMessage для своих целей.

Любая другая идея?

Спасибо вам

+0

Я не уверен, если я получаю вашу проблему правильно, но вы не можете просто обрабатывать 'OnMouseMove' для каждой внутренней панели у вас есть? Это может быть только один общий обработчик событий. – TLama

+0

@TLama Я получил ваше мнение, но эти панели будут полны других внутренних элементов управления, некоторые из которых будут созданы или размещены во время выполнения, и я не знаю их заранее. – yankee

+0

А, конечно, конечно. Кому нужны пустые панели. Глупый я :-) – TLama

ответ

2

Чтобы сделать уведомителя события перемещения мыши для всего кадра, независимо от того, какой ребенок управления наведен, вы можете написать обработчик для WM_SETCURSOR сообщения, как я узнал из TOndrej в this post. С помощью такого обработчика событий вы можете определить, какой элемент управления виден и вывести его на передний план.

Обратите внимание, что я сделал довольно часто используемую ошибку. Результат GetMessagePos не должен читаться таким образом. Это даже прямо упоминается в документах. У меня нет Windows SDK, чтобы увидеть MAKEPOINTS макросъемки, так что я буду это исправить позже:

type 
    TFrame1 = class(TFrame) 
    // there are many controls here; just pretend :-) 
    private 
    procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR; 
    end; 

implementation 

procedure TFrame1.WMSetCursor(var Msg: TWMSetCursor); 
var 
    MsgPos: DWORD; 
    Control: TWinControl; 
begin 
    inherited; 
    MsgPos := GetMessagePos; 
    Control := FindVCLWindow(Point(LoWord(MsgPos), HiWord(MsgPos))); 
    if Assigned(Control) then 
    Control.BringToFront; 
end; 
+0

Блестящий! Я никогда не изучал WM_SETCURSOR. Прежде чем прочитать свой ответ, я нашел способ успешно реализовать решение 2) моего первого вопроса, но это, конечно, лучшее решение! Спасибо. – yankee

+0

Добро пожаловать! Ну, я не знал о «WM_SETCURSOR» перед ответом Тондрея. Это довольно хорошо скрыто с его именем. – TLama

1

Я выложу это чувство собственного ответа только потому, что это работает, и это может быть полезно в некоторых случаях, но Я назвал TLama лучшим ответом.
Это решение 2) вопроса:

TMyFrame = class(TFrame) 
    // ...design time stuff... 
private 
    FMouseHovering: Boolean; 
    FPreviousOnAppMessage: TMessageEvent; 
    procedure DoOnAppMessage(var Msg: TMsg; var Handled: Boolean); 
protected 
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER; 
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE; 
public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
end; 


implementation 

constructor TMyFrame.Create(AOwner: TComponent); 
begin 
    inherited; 
    FMouseHovering := False; 
    FPreviousOnAppMessage := Application.OnMessage; 
    Application.OnMessage := DoOnAppMessage; 
end; 

destructor TMyFrame.Destroy; 
begin 
    Application.OnMessage := FPreviousOnAppMessage; 
    inherited; 
end; 

procedure TRiascoFrame.CMMouseEnter(var Message: TMessage); 
begin 
    FMouseHovering := True; 
end; 

procedure TRiascoFrame.CMMouseLeave(var Message: TMessage); 
begin 
    FMouseHovering := False; 
end; 

procedure TMyFrame.DoOnAppMessage(var Msg: TMsg; var Handled: Boolean); 
begin 
    if (Msg.message = WM_MOUSEMOVE) and FMouseHovering then 
    DoHandleMouseMove(Msg.hwnd, Integer(LoWord(Msg.lParam)), Integer(HiWord(Msg.lParam))); 
    if Assigned(FPreviousOnAppMessage) then 
    FPreviousOnAppMessage(Msg, Handled); 
end; 

procedure TMyFrame.DoHandleMouseMove(hWnd: HWND; X, Y: Integer); 
var 
    ClientPoint: TPoint; 
begin 
    ClientPoint := Point(X, Y); 
    Windows.ClientToScreen(hwnd, ClientPoint); 
    Windows.ScreenToClient(Self.Handle, ClientPoint); 
    if PtInRect(ClientRect, ClientPoint) then 
    begin 
    // ...do something... 
    end; 
end; 
+0

Таким образом, вы будете красть обработчик события OnMessage из единственного глобального экземпляра 'TApplication'. Подумайте, что произойдет, если у вас будет более одного такого фрейма. Каждый новый экземпляр этого фрейма украдет его из ранее созданного (не говоря о других элементах управления, которые могут его получить). – TLama

+0

Вот почему я сохранил в конструкторе предшествующий обработчик, назвал его в конце моего собственного обработчика и восстановил его в деструкторе. таким образом я создаю цепочку обработчиков. – yankee

+0

Я вижу, но только один обработчик может существовать одновременно, поэтому у вас не может быть более одного кадра такого типа. Если у вас было более одного, только последние созданные будут обрабатывать сообщения 'WM_MOUSEMOVE'. – TLama

Смежные вопросы