8. L'applicazione [SimuPaie] – versione 4 – ASP.NET / multi-vista / pagina singola
Letture consigliate: riferimento [1], Sviluppo Web con ASP.NET 1.1, sezioni:
-
Componenti server e controller delle applicazioni
-
Esempi di applicazioni MVC con componenti server ASP
Esamineremo ora una versione derivata dell'applicazione ASP.NET a tre livelli discussa in precedenza, che aggiunge nuove funzionalità. L'architettura della nostra applicazione si evolve come segue:
![]() |
L'elaborazione di una richiesta del client procede come segue:
- Il client invia una richiesta all'applicazione.
- L'applicazione elabora questa richiesta. Per farlo, potrebbe aver bisogno dell'assistenza del livello [business], che a sua volta potrebbe aver bisogno del livello [DAO] se è necessario scambiare dati con il database. L'applicazione riceve una risposta dal livello [business].
- Sulla base di questa risposta, seleziona (3) la vista (= la risposta) da inviare al cliente, fornendogli (4) le informazioni (il modello) di cui ha bisogno.
- La risposta viene inviata al client (5)
Si tratta di un'architettura web nota come MVC (Model–View–Controller):
- [Application] è il controller. Gestisce tutte le richieste del client.
- [Input, Simulazione, Simulazioni, ...] sono le viste. Una vista in .NET è codice ASP/HTML standard che contiene componenti che devono essere inizializzati. I valori che devono essere forniti a questi componenti formano il modello della vista.
In questa sede, implementeremo il modello di progettazione MVC come segue:
- le viste saranno componenti [View] all'interno di una singola pagina [Default.aspx]
- il controller è quindi il codice [Default.aspx.cs] per questa singola pagina.
Solo le applicazioni di base possono supportare questa implementazione MVC. Infatti, ad ogni richiesta, vengono istanziati tutti i componenti della pagina [Default.aspx], ovvero tutte le viste. Quando si invia la risposta, una di esse viene selezionata dal codice di controllo dell'applicazione semplicemente rendendo visibile il componente [View] corrispondente e nascondendo gli altri. Se l'applicazione ha molte viste, la pagina [Default.aspx] avrà molti componenti e il costo della loro istanziazione può diventare proibitivo. Inoltre, la modalità [Design] della pagina potrebbe diventare ingestibile a causa del numero eccessivo di viste. Questo tipo di architettura è adatto ad applicazioni con poche viste e sviluppate da una sola persona. Quando può essere adottata, consente lo sviluppo di un'architettura MVC in modo molto semplice. Questo è ciò che esploreremo in questa nuova versione.
8.1. Le viste dell'applicazione
Le diverse viste presentate all'utente saranno le seguenti:
- la vista [VueSaisies], che visualizza il modulo di simulazione

- la vista [VueSimulation], utilizzata per visualizzare i risultati dettagliati della simulazione:

- la vista [VueSimulations], che elenca le simulazioni eseguite dal cliente

- la vista [EmptySimulationsView], che indica che il client non ha simulazioni o non ne ha più:

- la vista [ErrorView], che indica uno o più errori:

8.2. Il progetto Visual Web Developer per il livello [web]
Il progetto Visual Web Developer per il livello [web] è il seguente:
![]() |
- in [1] troviamo:
- il file di configurazione dell'applicazione [Web.config] – è identico a quello dell'applicazione precedente.
- il file [Global.cs] che gestisce gli eventi dell'applicazione web, in questo caso l'avvio – è identico a quello dell'applicazione precedente, tranne per il fatto che gestisce anche l'avvio della sessione utente.
- il modulo [Default.aspx] dell'applicazione – contiene le varie viste dell'applicazione.
- In [2] si trovano i riferimenti al progetto – sono identici a quelli della versione precedente
8.3. Il file [Global.cs]
Il file [Global.cs], che gestisce gli eventi dell'applicazione web, è identico a quello dell'applicazione precedente, tranne per il fatto che gestisce anche l'avvio della sessione utente:
Global.cs
using System;
using System.Web;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
using System.Collections.Generic;
using istia.st.pam.web;
namespace pam_v4
{
public class Global : HttpApplication
{
// --- static application data ---
public static Employe[] Employes;
public static string Msg = string.Empty;
public static bool Erreur = false;
public static IPamMetier PamMetier = null;
// application startup
public void Application_Start(object sender, EventArgs e)
{
...
}
// start user session
public void Session_Start(object sender, EventArgs e)
{
// put an empty simulation list in the session
Session["simulations"] = new List<Simulation>();
}
}
}
- Righe 27–34: Gestiamo l'inizio della sessione. Inseriremo in questa sessione l'elenco delle simulazioni eseguite dall'utente.
- riga 30: viene creato un elenco vuoto di simulazioni. Una simulazione è un oggetto di tipo [Simulation], che descriveremo in dettaglio tra poco.
- riga 31: l'elenco delle simulazioni viene inserito nella sessione associata alla chiave "simulations"
8.4. La classe [Simulation]
Un oggetto di tipo [Simulation] viene utilizzato per incapsulare una riga della tabella delle simulazioni:

Il suo codice è il seguente:
namespace Pam.Web
{
public class Simulation
{
// simulation data
public string Nom { get; set; }
public string Prenom { get; set; }
public double HeuresTravaillees { get; set; }
public int JoursTravailles { get; set; }
public double SalaireBase { get; set; }
public double Indemnites { get; set; }
public double CotisationsSociales { get; set; }
public double SalaireNet { get; set; }
// manufacturers
public Simulation()
{
}
public Simulation(string nom, string prenom, double heuresTravailllees, int joursTravailles, double salaireBase, double indemnites, double cotisationsSociales, double salaireNet)
{
{
this.Nom = nom;
this.Prenom = prenom;
this.HeuresTravaillees = heuresTravailllees;
this.JoursTravailles = joursTravailles;
this.SalaireBase = salaireBase;
this.Indemnites = indemnites;
this.CotisationsSociales = cotisationsSociales;
this.SalaireNet = salaireNet;
}
}
}
}
I campi della classe corrispondono alle colonne della tabella di simulazione.
8.5. La pagina [Default.aspx]
8.5.1. Panoramica
La pagina [Default.aspx] contiene diversi componenti [View], uno per ogni vista. La sua struttura è la seguente:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="pam_v4.PagePam" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Simulateur de paie</title>
</head>
<body background="ressources/standard.jpg">
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
<asp:UpdatePanel runat="server" ID="UpdatePanelPam" UpdateMode="Conditional">
<ContentTemplate>
<table>
<tr>
<td>
<h2>
Simulateur de calcul de paie</h2>
</td>
<td>
<label>
 </label>
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
<img src="images/indicator.gif" />
<asp:Label ID="Label5" runat="server" BackColor="#FF8000"
EnableViewState="False" Text="Calcul en cours. Patientez ....">
</asp:Label>
</ProgressTemplate>
</asp:UpdateProgress>
</td>
<td>
<asp:LinkButton ID="LinkButtonFaireSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonFaireSimulation_Click">
| Faire la simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonEffacerSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonEffacerSimulation_Click">
| Effacer la simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonVoirSimulations" runat="server"
CausesValidation="False" OnClick="LinkButtonVoirSimulations_Click">
| Voir les simulations<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonFormulaireSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonFormulaireSimulation_Click">
| Retour au formulaire de simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonEnregistrerSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonEnregistrerSimulation_Click">
| Enregistrer la simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonTerminerSession" runat="server"
CausesValidation="False" OnClick="LinkButtonTerminerSession_Click">
| Terminer la session<br /></asp:LinkButton>
</td>
</table>
<hr />
<asp:MultiView ID="Vues1" ActiveViewIndex="0" runat="server">
<asp:View ID="VueSaisies" runat="server">
...
</asp:View>
</asp:MultiView>
<asp:MultiView ID="Vues2" runat="server">
<asp:View ID="VueSimulation" runat="server">
...
</asp:View>
<asp:View ID="VueSimulations" runat="server">
...
</asp:View>
<asp:View ID="VueSimulationsVides" runat="server">
...
</asp:View>
<asp:View ID="VueErreurs" runat="server">
...
</asp:View>
</asp:MultiView>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
- Riga 10: il tag per abilitare le estensioni Ajax
- righe 11–73: il contenitore UpdatePanel aggiornato dalle chiamate Ajax
- righe 12–72: il contenuto dell'UpdatePanel
- righe 13–52: l'intestazione che apparirà in ogni vista. Presenta all'utente un elenco di azioni possibili sotto forma di un elenco di collegamenti.
- riga 33: Notare l'attributo CausesValidation="False", che garantisce che i validatori della pagina non vengano eseguiti implicitamente quando si fa clic sul collegamento. Quando questo attributo viene omesso, il suo valore predefinito è True. La validazione della pagina può essere eseguita esplicitamente nel codice lato server utilizzando l'operazione Page.Validate.
- Righe 53–57: Un componente [MultiView] con una singola vista [VueSaisies]. Per questo motivo, il numero della vista da visualizzare è stato hardcoded nella riga 53: ActiveViewIndex="0"
- Righe 53–67: un componente [MultiView] con quattro viste: la vista [VueSimulation] (righe 59–61), la vista [VueSimulations] (righe 62–64), la vista [VueSimulationsVides] (righe 65–67) e la vista [VueErreurs] (righe 68–70). Un componente [MultiView] visualizza solo una vista alla volta. Per visualizzare la vista n. i del componente Vues2, scrivere il codice seguente:
8.5.2. L'intestazione en
L'intestazione è composta dai seguenti elementi:
![]() |
No. | Tipo | Nome | Ruolo |
LinkButton | LinkButtonEseguiSimulazione | richiede il calcolo della simulazione | |
LinkButton | LinkButtonClearSimulation | cancella il modulo di immissione dati | |
LinkButton | LinkButtonVisualizzaSimulazioni | visualizza l'elenco delle simulazioni già eseguite | |
LinkButton | LinkButtonSimulationForm | torna al modulo di immissione dati | |
LinkButton | LinkButtonSalvaSimulazione | salva la simulazione corrente nell'elenco delle simulazioni | |
LinkButton | LinkButtonEndSession | termina la sessione corrente |
8.5.3. La vista [ Saisies]
Il componente [View] denominato [VueSaisies] è il seguente:
![]() |
N. | Tipo | Nome | Ruolo |
Elenco a discesa | CasellaCombinataDipendenti | Contiene l'elenco dei nomi dei dipendenti | |
Casella di testo | Casella di testo Ore | Numero di ore lavorate – numero effettivo | |
Casella di testo | Casella di testo | Numero di giorni lavorati – numero intero | |
Validatore campo obbligatorio | ValidatoreCampoObbligatorioOre | verifica che il campo [2] [TextBoxHours] non sia vuoto | |
Validatore di espressioni regolari | RegularExpressionValidatorHours | verifica che il campo [2] [TextBoxHours] sia un numero reale >=0 | |
ValidatoreCampoObbligatorio | ValidatoreCampoObbligatorioGiorni | verifica che il campo [3] [TextBoxDays] non sia vuoto | |
Validatore di espressione regolare | RegularExpressionValidatorDays | verifica che il campo [3] [TextBoxDays] sia un numero intero >=0 |
8.5.4. La vista [Simulati on]
Il componente [View] denominato [SimulationView] è il seguente:

È costituito esclusivamente da componenti [Label] i cui ID sono elencati sopra.
8.5.5. La vista [Simu lations]
Il componente [View] denominato [SimulationView] è il seguente:
![]() |
N. | Tipo | Nome | Ruolo |
GridView | GridViewSimulazioni | Contiene l'elenco delle simulazioni |
Le proprietà del componente [GridViewSimulations] sono state definite come segue:
![]() |
- in [1]: clicca con il tasto destro del mouse sull'opzione [GridView] / [AutoFormat]
- in [2]: scegliere un tipo di visualizzazione per [GridView]
![]() |
- in [3]: selezionare le proprietà di [GridView]
- in [4]: modificare le colonne del [GridView]
- in [5]: aggiungere una colonna [BoundField] che sarà associata a una delle proprietà pubbliche dell'oggetto da visualizzare nella riga di [GridView]. L'oggetto visualizzato qui sarà un oggetto [Simulation].
![]() |
- in [6]: inserire il titolo della colonna
- in [7]: specificare il nome della proprietà della classe [Simulation] che sarà associata a questa colonna.
- [DataFormatString] specifica come devono essere formattati i valori visualizzati nella colonna.
Le colonne del componente [GridViewSimulations] presentano le seguenti proprietà:
N. | Proprietà |
Tipo: BoundField, HeaderText: Nome, DataField: Nome | |
Tipo: Campo associato, Testo intestazione: Cognome, Campo dati: Cognome | |
Tipo: Campo associato, Testo intestazione: Ore lavorate, Campo dati: OreLavorate | |
Tipo: Campo associato, Testo intestazione: Giorni lavorati, Campo dati: GiorniLavorati | |
Tipo: Campo associato, Testo intestazione: Stipendio base, Campo dati: StipendioBase, Stringa formato dati: {0:C} (formato valuta, C=Valuta) – visualizzerà il simbolo dell'euro. | |
Tipo: Campo associato, Testo intestazione: Indennità, Campo dati: Indemnites, Stringa formato dati: {0:C} | |
Tipo: Campo vincolato, Testo intestazione: Contributi previdenziali, Campo dati: ContributiPrevidenziali, Stringa formato dati: {0:C} | |
Tipo: Campo associato, Testo intestazione: Stipendio netto, Campo dati: StipendioNetto, Stringa formato dati: {0:C} |
Si noti che [DataField] deve corrispondere a una proprietà esistente della classe [Simulation]. Al termine di questa fase, sono state create tutte le colonne di tipo [BoundField]:
![]() |
- in [1]: le colonne create per [GridView]
- in [2]: la generazione automatica delle colonne deve essere disabilitata quando lo sviluppatore le definisce manualmente, come abbiamo appena fatto.
Dobbiamo ancora creare la colonna del link [Rimuovi]:

![]() |
- in [1]: aggiungere una colonna di tipo [CommandField / Delete]
- in [2]: ButtonType=Link per avere un link nella colonna anziché un pulsante
- in [3]: CausesValidation=False; cliccando sul link non verranno attivati eventuali controlli di validazione presenti nella pagina. Infatti, l'eliminazione di una simulazione non richiede alcuna validazione dei dati.
- in [4]: sarà visibile solo il link di eliminazione.
- in [5]: il testo di questo link
8.5.6. La vista [Simulazione e sVides]
Il componente [View] denominato [VueSimulationsVides] contiene semplicemente del testo:
8.5.7. La vista [E rrors]
Il componente [View] denominato [VueErreurs] è il seguente:
![]() |
N. | Tipo | Nome | Ruolo |
Ripetitore | RptErrors | visualizza un elenco di messaggi di errore |
Il componente [Repeater] consente di ripetere il codice ASP.NET/HTML per ogni oggetto in un'origine dati, in genere una raccolta. Questo codice è definito direttamente nel codice sorgente ASP.NET della pagina:
<asp:Repeater ID="RptErreurs" runat="server">
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
</asp:Repeater>
- Riga 2: <ItemTemplate> definisce il codice che verrà ripetuto per ogni elemento nell'origine dati.
- Riga 4: visualizza il valore dell'espressione Container.DataItem, che fa riferimento all'elemento corrente nell'origine dati. Poiché questo elemento è un oggetto, il metodo ToString di tale oggetto viene utilizzato per includerlo nell'output HTML della pagina. La nostra raccolta di oggetti sarà una raccolta List(Of String) contenente messaggi di errore. Le righe 3-5 includeranno sequenze <li>Message</li> nell'output HTML della pagina.
8.6. Il controller [Default.aspx.cs]
8.6.1. Panoramica
Torniamo all'architettura MVC dell'applicazione:
![]() |
- [Default.aspx.cs], che contiene il codice della singola pagina [Default.aspx], è il controller dell'applicazione.
- [Global] è l'oggetto [HttpApplication] che inizializza l'applicazione e ha un riferimento al livello [business].
Lo scheletro del codice del controller [Default.aspx.cs] è il seguente:
using System.Collections.Generic;
...
public partial class PagePam : Page
{
private void setVues(bool boolVues1, bool boolVues2, int index)
{
// display the requested views
// boolVues1 : true if Vues1 multi-view is to be visible
// boolVues1 : true if the Vues2 multiview is to be visible
// index: index of the Vues2 view to be displayed
...
}
private void setMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
{
// set menu options
// each Boolean is assigned to the Visible property of the corresponding link
...
}
// page loading
protected void Page_Load(object sender, System.EventArgs e)
{
// initial request processing
if (!IsPostBack)
{
...
}
}
protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
{
....
}
protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
{
...
}
protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
...
}
}
In risposta alla richiesta iniziale dell'utente (GET), viene gestito solo l'evento Load nelle righe 24–31. Per le richieste successive (POST) effettuate tramite i collegamenti del menu, vengono gestiti due eventi:
- l'evento Load (righe 24–31), ma il controllo booleano per Page.IsPostback (riga 27) garantisce che non accada nulla.
- l'evento associato al collegamento su cui è stato fatto clic:
![]() |
- righe 33–36: gestisce il clic sul link [1]
- righe 38–41: gestisce il clic sul link [2]
- righe 43-46: gestisci il clic sul link [3]
- righe 58-61: gestisci il clic sul link [4]
- righe 48–51: gestire il clic sul link [5]
- righe 53-56: gestisce il clic sul link [6]
Per estrapolare sequenze di codice che ricorrono frequentemente, sono stati creati due metodi interni:
- setVues, righe 7–14: imposta le viste da visualizzare
- setMenu, righe 16–21: imposta le opzioni di menu da visualizzare
8.6.2. L'evento Load
Letture consigliate: Riferimento [1], Sviluppo Web con ASP.NET 1.1:
- la sezione sul componente [Repeater] e il binding dei dati.
La prima vista presentata all'utente è quella del modulo vuoto:

L'inizializzazione dell'applicazione richiede l'accesso a un'origine dati, che potrebbe non riuscire. In questo caso, la prima pagina visualizzata è una pagina di errore:

L'evento [Load] viene gestito in modo simile alle versioni precedenti di ASP.NET:
// page loading
protected void Page_Load(object sender, System.EventArgs e)
{
// initial request processing
if (!IsPostBack)
{
// initialization errors?
if (Global.Erreur)
{
// display view [errors]
...
// menu positioning
...
return;
}
// loading employee names into the combo
...
// menu positioning
...
// view display [input]
...
}
}
Domanda: Completa il codice sopra riportato
8.6.3. Azione: Esegui la simulazione
Di seguito, la schermata (1) rappresenta la richiesta dell'utente, mentre la schermata (2) rappresenta la risposta inviata all'utente dall'applicazione web. Dalla schermata iniziale, l'utente può avviare una simulazione:


![]() |
La procedura che gestisce questa azione potrebbe essere simile alla seguente:
protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
{
// wage calculation
// valid page?
Page.Validate();
if (!Page.IsValid)
{
// view display [input]
...
return;
}
// the page is validated - inputs are retrieved
double HeuresTravaillées = ...;
int JoursTravaillés = ...;
// we calculate the employee's salary
FeuilleSalaire feuillesalaire;
try
{
feuillesalaire = ...
}
catch (PamException ex)
{
// display view [errors]
...
return;
}
// put the result in the session
Session["simulation"] = ...
// displaying results
...
// display views [entry, employee, salary]
...
// menu display
...
}
Domanda: Completa il codice sopra riportato
8.6.4. Azione: Salva la simulazione
Una volta completata la simulazione, l'utente può richiedere di salvarla:
![]() |

La procedura che gestisce questa azione potrebbe essere simile alla seguente:
protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
{
// save the current simulation in the list of simulations in the session
...
// the [simulations] view is displayed
...
}
Domanda: Completa il codice sopra riportato
8.6.5. Azione: Torna al modulo di simulazione
Letture consigliate: riferimento [1], [Sviluppo Web con ASP.NET 1.1]: il ruolo del campo nascosto _VIEWSTATE
Una volta visualizzato l'elenco delle simulazioni, l'utente può richiedere di tornare al modulo di simulazione:
![]() |
Si noti che la schermata (2) mostra il modulo così come è stato compilato. È importante ricordare che queste diverse visualizzazioni appartengono alla stessa pagina. Tra una richiesta e l'altra, i valori dei controlli vengono conservati dal meccanismo ViewState se la proprietà EnableViewState di tali controlli è impostata su true.
La procedura che gestisce questa azione potrebbe essere simile alla seguente:
protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
{
// view display [input]
...
// menu positioning
...
}
Domanda: Completa il codice sopra riportato
8.6.6. Azione: Cancella la simulazione
Una volta tornato al modulo di simulazione, l'utente può richiedere di cancellare le voci correnti:
![]() |
La procedura che gestisce questa azione potrebbe essere simile alla seguente:
protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
{
// RAZ of the form
...
}
Domanda: Completa il codice sopra riportato
8.6.7. Azione: Visualizza simulazioni
L'utente può richiedere di visualizzare le simulazioni che ha già creato:
![]() |
![]() |
La procedura che gestisce questa azione potrebbe essere simile alla seguente:
protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
{
// simulations are retrieved from the
...
// are there any simulations?
if (...)
{
// view [simulations] visible
...
}
else
{
// view [SimulationsVides]
...
}
// set the menu
...
}
Domanda: Completa il codice sopra
8.6.8. Azione: Elimina una simulazione
L'utente può richiedere l'eliminazione di una simulazione:
![]() |
![]() |
La procedura [GridViewSimulations_RowDeleting] che gestisce questa azione potrebbe essere simile alla seguente:
protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// simulations are retrieved from the
...
// delete the designated simulation (e.RowIndex is the number of the deleted line)
...
// are there any simulations left?
if (...)
{
// fill in the GridView
...
}
else
{
// view [SimulationsVides]
...
}
}
Domanda: Completa il codice sopra riportato
8.6.9. Azione: Termina sessione
L'utente può richiedere di terminare la propria sessione di simulazione. Ciò elimina il contenuto della sessione e visualizza un modulo vuoto:
![]() |
![]() |
La procedura che gestisce questa azione potrebbe essere simile alla seguente:
protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
{
// quit session
...
// display [entries] view
...
// menu positioning
...
}
Domanda: Completa il codice sopra riportato
Esercizio pratico: implementa l'applicazione web precedente sul tuo computer. Aggiungi la funzionalità Ajax.






















