- Learn
- Guida Delphi
- Applicazioni MDI (parte seconda)
Applicazioni MDI (parte seconda)
- di Carlo Marona
La procedura OpenFile si occupa di caricare il contenuto del file il cui nome gli viene passato come parametro. Il caricamento avviene sfruttando il metodo LoadFromFile della proprietà Lines (che è di tipo TStrings) dell’oggetto Doc (TMemo). Anche la procedura SaveFile utilizza un metodo del componente Doc. In questo caso si tratta del metodo SaveToFile della proprietà Lines. La procedura SaveFileAs utilizza prima di chiamare la procedura SaveFile una chiamata al metodo Execute dell’oggetto SaveDialog che permette, tramite la finestra di dialogo standard di windows, di selezionare il nome del file da assegnare al documento. Se il salvataggio avviene con successo, viene anche aggiornato il titolo della finestra contenente il documento con il nuovo nome di file.
In questa form, abbiamo anche un esempio di ridefinizione del costruttore della form.
La proprietà Modified definita in questa form, ci servirà per controllare lo stato di abilitazione delle azioni nella form principale. Il valore di questa proprietà viene restituito dal metodo GetModified che controlla se la proprietà dell’oggetto Doc (TMemo) è true o False. La classe TMemo infatti, sa riconoscere da sola quando il suo contenuto è stato modificato. Ecco invece come sono state impostate le proprietà dell’oggetto SaveDialog
DefaultExt = ‘.txt’
Filter = ‘Text files (*.txt)|*.txt|INI files (*.INI)|*.INI|
NFO files (*.NFO)|*.NFO|Any file (*.*)|*.*’
Title = ‘Save as’
Per impostare la proprietà filter è disponibile un editor di proprietà che si attiva cliccando sul tastino con i tre puntini vicino alla casella del valore della proprietà stessa. I filtri, sia nel componente OpenDialog che SaveDialog, servono a filtrare appunto i files in base alla loro estensione. La proprietà DefualtExt definisce invece l’estensione di default che verrà aggiunta al nome del quando questo verrà salvato senza specificarne l’estensione.
Passiamo ora a completare la form principale. Aggiungiamo prima altre azioni prendendole questa volta tra quelle predefinite che ci mette a disposizione Delphi. Sempre dall’editor di proprietà dell’Oggetto ActionList, scegliamo “New standard action” dal menu a tendina che viene visualizzato premendo il tasto con la freccia verso il basso posto vicino al tasto “New action”. Selezioniamo tutte le azioni che fanno parte della categoria “Window” selezionando la prima e tenendo premuto shift selezionando l’ultima. In questo modo avremo creato delle azioni che serviranno, senza scrivere nemmeno una riga di codice a gestire la funzioni relative alla finestre figlie della nostra applicazione. Aggiungiamo ancora un componente, il dialogo di apertura file (TOpenDialog) chiamandolo OpenDialog e settando le proprietà seguenti
DefaultExt = ‘.txt’
Filter = ‘Text files (*.txt)|*.txt|INI files (*.INI)|*.INI|
NFO files (*.NFO)|*.NFO|Any file (*.*)|*.*’
Title = ‘Open’
Come per la form EditorWin, vediamo quale è il codice da inserire nella definizione della classe TMain
type
TMain = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
ActionList1: TActionList;
actOpen: TAction;
actSave: TAction;
actSaveAs: TAction;
actNew: TAction;
New1: TMenuItem;
Save1: TMenuItem;
SaveAs1: TMenuItem;
N1: TMenuItem;
Exit1: TMenuItem;
OpenDialog: TOpenDialog;
Open1: TMenuItem;
Windows1: TMenuItem;
WindowArrange1: TWindowArrange;
WindowCascade1: TWindowCascade;
WindowClose1: TWindowClose;
WindowMinimizeAll1: TWindowMinimizeAll;
WindowTileHorizontal1: TWindowTileHorizontal;
WindowTileVertical1: TWindowTileVertical;
Arrange1: TMenuItem;
Cascade1: TMenuItem;
MinimizeAll1: TMenuItem;
TileHorizontally1: TMenuItem;
TileVertically1: TMenuItem;
N2: TMenuItem;
Close1: TMenuItem;
procedure CheckSaveEnabledState(Sender: TObject);
procedure Exit1Click(Sender: TObject);
procedure actNewExecute(Sender: TObject);
procedure actOpenExecute(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure actSaveExecute(Sender: TObject);
procedure actSaveAsExecute(Sender: TObject);
private
{ Private declarations }
NewDocCount : Integer;
Function CheckModified : Boolean;
Function NewDocument : Boolean;
Function OpenDocument(FileName : TFileName) : Boolean;
public
{ Public declarations }
end;
Anche qui il codice in rosso è quello gestito automaticamente da Delphi e quello in verde quello che invece dobbiamo inserire noi.
Ecco di seguito il codice da inserire
function TMain.CheckModified: Boolean;
begin
Result := False;
If Assigned(ActiveMDIChild) then
Result := TEditorWin(ActiveMDIChild).Modified;
end;
procedure TMain.CheckSaveEnabledState(Sender: TObject);
begin
TAction(Sender).Enabled := CheckModified;
end;
procedure TMain.Exit1Click(Sender: TObject);
begin
Close;
end;
procedure TMain.actNewExecute(Sender: TObject);
begin
If Not NewDocument then
MessageDlg(‘Error creating new document!’, mtError, [mbOk], 0);
end;
function TMain.NewDocument: Boolean;
Var NewForm : TEditorWin;
begin
try
Inc(NewDocCount);
NewForm := TEditorWin.Create(Self, NewDocCount, true, ”);
NewForm.Show;
Result := true;
Except
Result := False;
End;
end;
procedure TMain.actOpenExecute(Sender: TObject);
Begin
If OpenDialog.Execute then
OpenDocument(OpenDialog.FileName);
end;
function TMain.OpenDocument(FileName : TFileName): Boolean;
Var OpenForm : TEditorWin;
begin
try
OpenForm := TEditorWin.Create(Self, 0, False, FileName);
OpenForm.Show;
Result := true;
Except
Result := False;
End;
end;
Procedure TMain.FormCreate(Sender: TObject);
begin
Caption := Application.Title;
end;
procedure TMain.actSaveExecute(Sender: TObject);
begin
If Assigned(ActiveMDIChild) then
TEditorWin(ActiveMDIChild).SaveDoc;
end;
procedure TMain.actSaveAsExecute(Sender: TObject);
begin
If Assigned(ActiveMDIChild) then
TEditorWin(ActiveMDIChild).SaveDocAs;
end;
Il metodo privato CheckModified si occupa di controllare se la finestra EditorWin correntemente attivata abbia o no delle pendenze riguardo a modifiche apportate al contenuto del documento. tramite la proprietà ActiveMDIChild della form frame, si ottiene un riferimento di tipo TForm alla form MDIChild attiva, ovvero la finestra che possiede il fuoco. Poichè la nostra finestra child è una classe di tipo TEditorWin, per accedere alla sua proprietà Modified, non presente nella classe TForm, occorre fare un type cast, una conversione di tipo a TEditorWin. Lo stesso metodo viene utilizzato anche negli altri metodi che ottengono il riferimento tramite ActiveMDIChild.
Nella funzione NewDocument che si occupare di creare un nuovo documento vuoto, abbiamo utilizzato un contatore per distinguere i diversi documenti creati nell’ambito della stessa sessione di lavoro. Questo viene passato al costruttore della form EditorWin che lo utilizzerà per aggiornare il titolo della finestra stessa impostandone la proprietà Caption.
Questo abbozzo di editor può essere ampliato aggiungendo toolbars, statusbar, la stampa e così via. Potrebbe essere un buon esercizio completarlo con quello che manca. I file del progetto, per una consultazione più accurata sono disponibili qui.
Un approfondimento sulle finestre MDI.
Abbimo visto che per utilizzare questo tipo di struttura in Delphi è molto semplice, è sufficiente impostare per la finestra frame il valore della proprietà FormStyle a fsMDIForm e per le finestre figlie a fsMDIChild. Tecnicamente, la prima finestra, quella che contiene le altre, è formata da due finestre sovrapposte. La prima è il frame vero e proprio, la tipica form di Delphi, l’altra è una finestra che non ha bordo ne titolo che ricopre l’intera area client della finestra frame. Tutte le finestre figlie che vengono create, sono figlie della finestra senza bordi, chiamata Client. Per cambiare il colore dello fondo della finestra Client, ovvero della area client della nostra finestra frame, è sufficiente, in Delphi, impostare la proprietà Color della form Frame, come è visibile nei sorgenti dell’esempio realizzato. In definitiva possiamo riassumere così la gerarchia delle finestre MDI
Finestra Frame -> Finestra Client -> Finestre Figlie
Ecco alcune proprietà e metodi della classe TForm che non sono stati utilizzati nel nostro esempio ma che meritano ci essere mensionati. La classe TForm definisce due metodi che permettono di selezionare le finestre figlie attive relativamente alla finestra attiva. Per la precisione, il metodo Next attiva la finestra figlia successiva, nella lista interna delle finestre figlie, a quella correntemente attiva. Al contrario, il metodo Previus permette di selezionare la finestra precedente a quella correntemente attiva.
La proprietà ClientHandle contiene il riferimento (Window Handle) alla finestra Client di cui abbiamo parlato sopra.
Le proprietà MDIChildCount e MDIChildren permettono di operare sulle finestre figlie. In paricolare, MDIChildCount restituisce il numero corrente delle finestre figlie e MDIChildren è un array di riferimenti alle finestre figlie. Queste due proprietà utilizzate in combinazione, permettono di scorrere ed operare sulle finestre figlie correnti.
Un’altra caratteristica molto interessante, che non riguarda solamente le applicazioni MDI, è la possibilità di gestire l’automerge dei menu delle varie finestre. Avendo una finestra principale con un menu, è possibile far disporre automaticamente il menu di una finestra secondaria definendo un ordinamento per i singoli menu. Per la trattazione di questo argomento si ramanda alla documentazione del linguaggio oppure alla ottima guida in linea dell’IDE.
Una cosa da tenere presente durante la realizzazione di una applicazione MDI è che, per il funzionamento visto precedentemente, per applicare una immagine di sfondo alla finestra principale dell’applicazione (finestra frame), non è sufficiente disegnare l’immagine sulla form poichè questa viene coperta dalla finestra Client. Per ovviare a questo problema occorre ricorre a una pratica, diciamo di basso livello. Chi programma in ambiente Windows sa che ogni finestra è legata ad una Window Procedure. Che cos’è una window procedure? Una window Procedure non è altro che la procedura che consente alla finestra di gestire i messaggi a lei inviati: ridimensionamento, ridisegno, posizionamento, etc. Questa pratica prevede di sostituire questa Window Procedure per alterare il comportamento standard della finestra. Quello che dobbiamo fare è intercettare il messaggio WM_ERASEBKGND inviato alla nostra finestra quando è necessario il ridisegno dello sfondo. Per fare ciò, occorre salvare il riferimento alla vecchia Window Procedure e sostituire la procedura originale con la nostra che sarà in grado di gestire disegno dell’immagine nella finestra Client. Il procedimento completo è rintracciabile sul libro di Marco Cantù “Programmare con Delphi 5”.
Se vuoi aggiornamenti su Applicazioni MDI (parte seconda) inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
I Video di HTML.it
Unit testing per Android
Pietro Alberto Rossi ci suggerisce alcuni tool utili per approcciare il testing de codice, in particolare per progetti Android