Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Spostamento e ridimensionamento di componenti visuali su una form: esempio di emulazione di un ambiente MDI


In questo articolo vado ad esporre una soluzione alternativa al classico approccio per la creazione di ambienti MDI. Viene in particolar modo esaminata la tecnica di spostamento e ridimensionamento di componenti grafici posti su una Form (come quando si mette ad esempio un pannello su una Form nell' IDE di Delphi). La finestra MDI che fa da contenitore per le finestre MDI figlie (FormStyle = fsMDIForm) può essere emulata da un componente TScrollBox, mentre le finestre MDI child (FormStyle = fsMDIChild) possono essere emulate da un componente TPanel, il cui parent è il componente TScrollBox, con un componente figlio di tipo TPanel che va ad emulare la Caption della Form. Il pannello di emulazione della Caption avrà le seguenti caratteristiche

1) Align := alTop;
2) BevelOuter := bvNone;
3) Height := 21;
4) Alignment := taLeftJustify;
5) Caption := 'ChildForm1';
6) Color := clActiveCaption;
7) Font.Color := clCaptionText;


Userò poi la seguente notazione:

Form Panel: il pannello che emula la Form figlia
Caption Panel: il pannello di emulazione della Caption della Form figlia (il suo Parent è Form Panel)

Form Panel deve essere ridimensionabile proprio come qualsiasi Form con BorderStyle bsSizeable (il valore di default della proprietà BorderStyle). Per raggiungere quest' obiettivo dobbiamo intercettare il messaggio di Windows wm_NcHitTest. Il messaggio WM_NCHITTEST viene inviato ad una finestra quando si sposta il cursore su quella finestra oppure quando un pulsante del mouse è premuto o rilasciato sulla finestra. Ha 2 parametri: XPos and YPos. Rappresentano le coordinate (x,y) (relativamente all' angolo in alto a sinistra dello schermo) del cursore. In una situazione normale (cioè quando non è stata definita una gestione personalizzata di questo messaggio) quando questo messaggio viene inviato ad una Form, viene processato dalla funzione DefWindowProc che restituisce i seguenti valori che indicano la posizione del cursore:

HTBOTTOM: nel bordo orizzontale in basso della finestra
HTBOTTOMLEFT: nell' angolo in basso a sinistra della finestra
HTBOTTOMRIGHT: nell' angolo in basso a destra della finestra
HTTOP: nel bordo orizzontale in alto della finestra
HTTOPLEFT: nell' angolo in alto a sinistra della finestra
HTTOPRIGHT: nell' angolo in alto a destra della finestra
HTLEFT: nel bordo verticale a sinistra della finestra
HTRIGHT: nel bordo verticale a destra della finestra
...
...

(rimando al Platform SDK per altri valori).

Nel messaggio c' è anche un campo Result che va settato, all' interno della procedura che rileva il messagio, per definire il comportamento.

Se il risultato è HTBOTTOM or HTTOP, la freccia del cursore sarà una freccia verticale:
Se premo il pulsante sinistro del mouse e sposto il mouse, la Form sarà ridimensionata in ampiezza.

Se il risultato è HTRIGHT or HTLEFT, la freccia del cursore sarà una freccia orizzontale:
Se premo il pulsante sinistro del mouse e sposto il mouse, la Form sarà ridimensionata in altezza.

Se il risultato è HTBOTTOMLEFT or HTBOTTOMRIGHT or HTTOPLEFT or HTTOPRIGHT,
la freccia del cursore sarà una freccia diagonale: se premo il pulsante sinistro del mouse e sposto il mouse, la Form sarà ridimensionata sia in ampiezza sia in altezza.

Se vogliamo raggiungere il medesimo risultato in un componente TPanel bisogna fare l' overriding della sua Window Procedure ed impostare il Result. Tutto ciò può essere raggiunto nel seguente modo:

type TSzblPanel = class (TPanel) public procedure WmNcHitTest (var Msg: TWmNcHitTest); message wm_NcHitTest; end; procedure TSzblPanel.WmNcHitTest(var Msg: TWmNcHitTest); begin with ScreenToClient(Point (Msg.XPos, Msg.YPos)) do begin if (x < 5) and (y < 5) then Msg.Result := htTopLeft else if (x > Width - 5) and (y < 5) then Msg.Result := htTopRight else if (x > Width - 5) and (y > Height - 5) then Msg.Result := htBottomRight else if (x < 5) and (y > Height - 5) then Msg.Result := htBottomLeft else if (x < 5) then Msg.Result := htLeft else if (y < 5) then Msg.Result := htTop else if (x > Width - 5) then Msg.Result := htRight else if (y > Height - 5) then Msg.Result := htBottom else inherited; end; end;

Ora occorre derivare il componente TSizePanel per dargli il Caption Panel. Se premo il pulsante sinistro del mouse sul Caption panel e sposto il mouse, il Form Panel verrà spostato conseguentemente. Un pannello può essere trascinato implementando il suo evento OnMouseDown:

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); const kSC_DragMove = $F012 ; var rPnl : TPanel ; begin rPnl := TPanel(Sender); ReleaseCapture(); // invia il messaggio al controllo su cui stiamo facendo click rPnl.Perform( WM_SYSCOMMAND, kSC_DragMove, 0 ); end;

E' da notare che voglio trascinare il Form Panel all' interno del componente TScrollBox e non il Caption panel all' interno del Form Panel. Creiamoci quindi il componente derivato da TSzblPanel che ci consente di emulare in pieno una Child Form

type TFormPanel = class(TSzblPanel) private FCaptionPanel: TPanel; procedure SetCaptionTitle(Title: string); function GetCaptionTitle(): string; protected procedure CaptionPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); constructor Create(Aowner: TComponent); override; public { Public declarations } published { Published declarations } property CaptionTitle: string read GetCaptionTitle write SetCaptionTitle; end; constructor TFormpanel.Create(AOwner: TComponent); begin inherited; Borderwidth := 3; FcaptionPanel := TPanel.Create(Self); with FCaptionPanel do begin Parent := Self; Align := alTop; BevelOuter := bvNone; Height := 21; Alignment := taLeftJustify; Caption := 'ChildForm1'; Color := clActiveCaption; Font.Color := clCaptionText; //assegna l' evento "OnMouseDown" per trascinare "Form Panel" OnMouseDown := CaptionPanelMouseDown; end; end; procedure TFormPanel.CaptionPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); const kSC_DragMove = $F012 ; var rPnl : TPanel ; begin //vogliamo trascinare il parent del "Caption Panel" //e non il "Caption Panel" stesso così sostituiamo // rPnl := TPanel(Sender); //con // rPnl := TPanel(TPanel(Sender).Parent) ; rPnl := TPanel(TPanel(Sender).Parent) ; ReleaseCapture(); // invia il messaggio al parent del controllo su cui stiamo facendo click. rPnl.Perform( WM_SYSCOMMAND, kSC_DragMove, 0 ); end; procedure TFormPanel.SetCaptionTitle(Title: string); begin FCaptionPanel.Caption := Title; end; function TFormPanel.GetCaptionTitle(): string; begin result := FCaptionPanel.Caption; end;

Di seguito il codice sorgente completo del componente che emula una Child Form

unit FormPanel; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls; type TSzblPanel = class (TPanel) public procedure WmNcHitTest (var Msg: TWmNcHitTest); message wm_NcHitTest; end; type TFormPanel = class(TSzblPanel) private FCaptionPanel: TPanel; procedure SetCaptionTitle(Title: string); function GetCaptionTitle(): string; protected procedure CaptionPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); constructor Create(Aowner: TComponent); override; public { Public declarations } published { Published declarations } property CaptionTitle: string read GetCaptionTitle write SetCaptionTitle; end; procedure Register; implementation procedure TSzblPanel.WmNcHitTest(var Msg: TWmNcHitTest); begin with ScreenToClient(Point (Msg.XPos, Msg.YPos)) do begin if (x < 5) and (y < 5) then Msg.Result := htTopLeft else if (x > Width - 5) and (y < 5) then Msg.Result := htTopRight else if (x > Width - 5) and (y > Height - 5) then Msg.Result := htBottomRight else if (x < 5) and (y > Height - 5) then Msg.Result := htBottomLeft else if (x < 5) then Msg.Result := htLeft else if (y < 5) then Msg.Result := htTop else if (x > Width - 5) then Msg.Result := htRight else if (y > Height - 5) then Msg.Result := htBottom else inherited; end; end; constructor TFormpanel.Create(AOwner: TComponent); begin inherited; Borderwidth := 3; FcaptionPanel := TPanel.Create(Self); with FCaptionPanel do begin Parent := Self; Align := alTop; BevelOuter := bvNone; Height := 21; Alignment := taLeftJustify; Caption := 'ChildForm1'; Color := clActiveCaption; Font.Color := clCaptionText; OnMouseDown := CaptionPanelMouseDown; end; end; procedure TFormPanel.CaptionPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); const kSC_DragMove = $F012 ; var rPnl : TPanel ; begin rPnl := TPanel(TPanel(Sender).Parent) ; ReleaseCapture(); rPnl.Perform( WM_SYSCOMMAND, kSC_DragMove, 0 ); end; procedure TFormPanel.SetCaptionTitle(Title: string); begin FCaptionPanel.Caption := Title; end; function TFormPanel.GetCaptionTitle(): string; begin result := FCaptionPanel.Caption; end; procedure Register; begin RegisterComponents('Carlo Pasolini', [TFormPanel]); end; end.

 

 

 

 


 

 

 

 

 

 
 
Your Ad Here