Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 32 di 43
  • livello principiante
Indice lezioni

Applicazioni MDI (parte seconda)

Le Multiple Document Interface: aprire e gestire i file
Le Multiple Document Interface: aprire e gestire i file
Link copiato negli appunti

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".

Ti consigliamo anche