8. Додаток [SimuPaie] – версія 4 – ASP.NET / багатовидовий / односторінковий
Рекомендовані матеріали: довідник [1], Розробка WEB з ASP.NET 1.1, розділи:
-
Серверні компоненти та контролер додатків
-
Приклади додатків MVC із серверними компонентами ASP
Зараз ми розглядаємо похідну версію трирівневого додатка ASP.NET, який ми вивчали раніше, з доданими новими функціональними можливостями. Архітектура нашого додатка змінюється наступним чином:
![]() |
Обробка запиту клієнта відбувається за такими етапами:
- клієнт надсилає запит до додатка.
- додаток обробляє цей запит. Для цього їй може знадобитися допомога шару [métier], якому, у свою чергу, може знадобитися шар [dao], якщо необхідно обмінятися даними з базою даних. Додаток отримує відповідь від шару [métier].
- Згідно з нею, воно вибирає (3) представлення (= відповідь), яке слід надіслати клієнту, надаючи йому (4) необхідну інформацію (шаблон).
- Відповідь надсилається клієнту (5)
Тут ми маємо веб-архітектуру, відому як MVC (Модель – Представлення – Контролер):
- [Application] — це контролер. Він обробляє всі запити клієнта.
- [Saisies, Simulation, Simulations, ...] — це представлення. Представлення у форматі .NET — це стандартний код ASP / HTML, що містить компоненти, які потрібно ініціалізувати. Значення, які потрібно надати цим компонентам, утворюють модель подання.
Тут ми реалізуємо шаблон проектування (design pattern) MVC наступним чином:
- представленнями будуть компоненти [View] у межах єдиної сторінки [Default.aspx]
- контролер — це код [Default.aspx.cs] цієї єдиної сторінки.
Лише базові додатки можуть підтримувати цю реалізацію MVC. Адже при кожному запиті інстанціюються всі компоненти сторінки [Default.aspx], а отже, і всі види. Під час відправлення відповіді код управління додатком обирає один із них, просто роблячи видимим відповідний компонент [View] та приховуючи інші. Якщо додаток має багато подання, сторінка [Default.aspx] матиме багато компонентів, і вартість їх інстанціювання може стати надто високою. Крім того, режим роботи сторінки [Design] може стати некерованим через надмірну кількість подання. Цей тип архітектури підходить для додатків з невеликою кількістю подань, які розробляє одна людина. Коли її можна застосувати, вона дозволяє дуже просто розробити архітектуру MVC. Саме це ми й розглянемо в цій новій версії.
8.1. Види додатка
Користувачеві будуть представлені такі види:
- вікно [VueSaisies], яке відображає форму моделювання

- екран [VueSimulation], що використовується для відображення детальних результатів моделювання:

- погляд [VueSimulations], що містить перелік моделювань, виконаних клієнтом

- вигляд [VueSimulationsVides], який вказує, що у клієнта немає або більше немає моделювань:

- перегляд [VueErreurs], який вказує на одну або кілька помилок:

8.2. Проєкт Visual Web Developer шару [web]
Проєкт Visual Web Developer шару [web] виглядає наступним чином:
![]() |
- у [1] міститься:
- файл конфігурації [Web.config] додатка — ідентичний файлу попереднього додатка.
- файл [Global.cs], який керує подіями веб-додатка, зокрема його запуском, — ідентичний файлу попереднього додатка, за винятком того, що він також керує запуском сеансу користувача.
- форма [Default.aspx] додатка — містить різні види додатка.
- У файлі [2] містяться посилання на проект — вони ідентичні тим, що були у попередній версії
8.3. Файл [Global.cs]
Файл [Global.cs], який керує подіями веб-додатка, ідентичний файлу попереднього додатка, за винятком того, що він додатково керує запуском сеансу користувача:
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
{
// --- статичні дані додатка ---
public static Employe[] Employes;
public static string Msg = string.Empty;
public static bool Erreur = false;
public static IPamMetier PamMetier = null;
// запуск програми
public void Application_Start(object sender, EventArgs e)
{
...
}
// запуск сеансу користувача
public void Session_Start(object sender, EventArgs e)
{
// до сеансу додається порожній список симуляцій
Session["simulations"] = new List<Simulation>();
}
}
}
- рядки 27–34: тут обробляється запуск сеансу. У цей сеанс ми помістимо список симуляцій, виконаних користувачем.
- рядок 30: створюється порожній список симуляцій. Симуляція — це об’єкт типу [Simulation], який ми детально розглянемо трохи пізніше.
- рядок 31: список симуляцій розміщується в сесії, пов’язаній із ключем «simulations»
8.4. Клас [Simulation]
Об’єкт типу [Simulation] використовується для інкапсуляції рядка таблиці симуляцій:

Його код такий:
namespace Pam.Web
{
public class Simulation
{
// дані симуляції
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; }
// конструктори
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;
}
}
}
}
Поля класу відповідають стовпцям таблиці симуляцій.
8.5. Сторінка [Default.aspx]
8.5.1. Загальний огляд
Сторінка [Default.aspx] містить кілька компонентів [View], по одному для кожного виду. Її каркас має такий вигляд:
<%@ 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>
- рядок 10: тег для розміщення розширень Ajax
- рядки 11–73: контейнер UpdatePanel, що оновлюється за допомогою викликів Ajax
- рядки 12–72: вміст контейнера UpdatePanel
- рядки 13–52: заголовок, який буде присутній у кожному вигляді. Він показує користувачеві перелік можливих дій у вигляді списку посилань.
- рядок 33: зверніть увагу на атрибут CausesValidation="False", завдяки якому валідатори сторінки не будуть виконуватися неявно при натисканні на посилання. Якщо цей атрибут відсутній, його значенням за замовчуванням є True. Перевірку сторінки можна виконати явно в коді, що виконується на стороні сервера, за допомогою операції Page.Validate.
- рядки 53–57: компонент [MultiView] з єдиним видом [VueSaisies]. З цієї причини у рядку 53 було жорстко задано номер виду, що відображається: ActiveViewIndex="0"
- рядки 53–67: компонент [MultiView] із чотирма видами: вид [VueSimulation] у рядках 59–61, вид [VueSimulations] у рядках 62–64, вид [VueSimulationsVides] у рядках 65–67, вид [VueErreurs] у рядках 68–70. Компонент [MultiView] відображає лише один вигляд за раз. Щоб відобразити вигляд № i компонента Vues2, слід написати такий код:
8.5.2. Заголовок « »
Заголовок складається з таких компонентів:
![]() |
№ | Тип | Назва | Роль |
LinkButton | LinkButtonFaireSimulation | запит на розрахунок симуляції | |
LinkButton | LinkButtonEffacerSimulation | очистити форму введення даних | |
LinkButton | LinkButtonVoirSimulations | відображає список вже виконаних моделювань | |
LinkButton | LinkButtonFormulaireSimulation | повертає до форми введення даних | |
LinkButton | LinkButtonEnregistrerSimulation | зберігає поточну симуляцію у списку симуляцій | |
LinkButton | LinkButtonTerminerSession | завершує поточну сесію |
8.5.3. Вигляд [Saisies]
Компонент [View], названий [VueSaisies], має такий вигляд:
![]() |
№ | Тип | Назва | Роль |
DropDownList | ComboBoxEmployes | Містить список імен співробітників | |
TextBox | TextBoxHeures | Кількість відпрацьованих годин — фактична кількість | |
TextBox | TextBoxJours | Кількість відпрацьованих днів – ціле число | |
RequiredFieldValidator | RequiredFieldValidatorHeures | перевіряє, чи поле [2] [TextBoxHeures] не є порожнім | |
RegularExpressionValidator | RegularExpressionValidatorHeures | перевіряє, чи поле [2] [TextBoxHeures] є дійсним числом >=0 | |
RequiredFieldValidator | RequiredFieldValidatorJours | перевіряє, чи поле [3] [TextBoxJours] не є порожнім | |
RegularExpressionValidator | RegularExpressionValidatorJours | перевіряє, чи поле [3] [TextBoxJours] є цілим числом >=0 |
8.5.4. Поле [Simulation]
Компонент [View] з назвою [VueSimulation] має такий вигляд:

Він складається виключно з компонентів [Label], серед яких ID зазначені вище.
8.5.5. Вигляд [Simulations]
Компонент [View], названий [VueSimulations], має такий вигляд:
![]() |
№ | Тип | Назва | Роль |
GridView | GridViewSimulations | Містить список симуляцій |
Властивості компонента [GridViewSimulations] визначено таким чином:
![]() |
- у [1]: клацніть правою кнопкою миші на [GridView] / опція [Mise en forme automatique]
- на [2]: вибрати тип відображення для [GridView]
![]() |
- у [3]: вибрати властивості для [GridView]
- в [4]: редагувати стовпці [GridView]
- у [5]: додати стовпець типу [BoundField], який буде пов'язаний (bound) з однією з публічних властивостей об'єкта, що відображається у рядку [GridView]. Об’єкт, що відображається тут, буде об’єктом типу [Simulation].
![]() |
- у [6]: вкажіть назву стовпця
- у [7]: вказати назву властивості класу [Simulation], яка буде пов’язана з цим стовпцем.
- [DataFormatString] вказує, як мають бути відформатовані значення, що відображаються у стовпці.
Стовпці компонента [GridViewSimulations] мають такі властивості:
№ | Властивості |
Тип: BoundField, HeaderText: Ім’я, DataField: Ім’я | |
Тип: BoundField, HeaderText: Прізвище, DataField: Ім’я | |
Тип: BoundField, HeaderText: Відпрацьовані години, DataField: HeuresTravaillees | |
Тип: BoundField, HeaderText: Відпрацьовані дні, DataField: JoursTravailles | |
Тип: BoundField, HeaderText: Базова заробітна плата, DataField: SalaireBase, DataFormatString: {0:C} (валютний формат, C = валюта) — відображатиме символ євро. | |
Тип: BoundField, HeaderText: Допомога, Поле даних: Indemnites, DataFormatString: {0:C} | |
Тип: BoundField, HeaderText: Соціальні внески, DataField: CotisationsSociales, DataFormatString: {0:C} | |
Тип: BoundField, HeaderText: Чиста заробітна плата, DataField: SalaireNet, DataFormatString: {0:C} |
Слід звернути увагу на те, що поле [DataField] має відповідати існуючій властивості класу [Simulation]. За підсумками цього етапу було створено всі стовпці типу [BoundField]:
![]() |
- в [1]: стовпці, створені для [GridView]
- в [2]: автоматичне створення стовпців має бути вимкнено, коли розробник сам їх визначає, як ми щойно зробили.
Залишилося створити стовпець посилань [Retirer]:

![]() |
- у [1]: додати стовпець типу [CommandField / Supprimer]
- у [2]: ButtonType=Link, щоб у стовпці було посилання, а не кнопка
- у [3]: CausesValidation=False — клік на посилання не призведе до виконання перевірок, які можуть бути на сторінці. Адже видалення симуляції не вимагає перевірки даних.
- у [4]: буде видно лише посилання на видалення.
- у [5]: текст цього посилання
8.5.6. Вигляд [SimulationsVides]
Компонент [View] із назвою [VueSimulationsVides] містить лише текст:
8.5.7. Вигляд [Erreurs]
Компонент [View] з назвою [VueErreurs] має такий вигляд:
![]() |
№ | Тип | Назва | Роль |
Ретранслятор | RptErreurs | відображає список повідомлень про помилки |
Компонент [Repeater] дозволяє повторювати код ASP.NET / HTML для кожного об’єкта джерела даних, зазвичай колекції. Цей код визначається безпосередньо у вихідному коді ASP.NET на сторінці:
<asp:Repeater ID="RptErreurs" runat="server">
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
</asp:Repeater>
- рядок 2: <ItemTemplate> визначає код, який буде повторюватися для кожного елемента джерела даних.
- рядок 4: відображає значення виразу Container.DataItem, який позначає поточний елемент джерела даних. Оскільки цей елемент є об’єктом, для його включення до потоку HTML на сторінці використовується метод ToString цього об’єкта. Наша колекція об’єктів буде колекцією List(Of String), що містить повідомлення про помилки. У рядках 3–5 послідовності <li>Message</li> будуть включені до потоку HTML сторінки.
8.6. Контролер [Default.aspx.cs]
8.6.1. Загальний огляд
Повернемося до архітектури MVC додатка:
![]() |
- [Default.aspx.cs] — це код управління окремою сторінкою, а [Default.aspx] — контролер додатка.
- [Global] — це об’єкт типу [HttpApplication], який ініціалізує додаток і має посилання на рівень [métier].
Структура коду контролера [Default.aspx.cs] така:
using System.Collections.Generic;
...
public partial class PagePam : Page
{
private void setVues(bool boolVues1, bool boolVues2, int index)
{
// відображаються запитувані види
// boolVues1: true, якщо багатовидовий перегляд «Види1» має бути видимим
// boolVues1: true, якщо мультивигляд «Види2» має бути видимим
// index: індекс виду з «Vues2», який потрібно відобразити
...
}
private void setMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
{
// встановлюються параметри меню
// кожен логічний параметр присвоюється властивості Visible відповідного посилання
...
}
// завантаження сторінки
protected void Page_Load(object sender, System.EventArgs e)
{
// обробка початкового запиту
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)
{
...
}
}
При першому запиті (GET) користувача обробляється лише подія Load у рядках 24–31. При наступних запитах (POST), зроблених через посилання в меню, обробляються дві події:
- подія Load (24–31), але перевірка логічного значення Page.IsPostback (рядок 27) призводить до того, що ніяких дій не буде виконано.
- подія, пов’язана з посиланням, на яке натиснули:
![]() |
- рядки 33–36: обробляють клік на посилання [1]
- рядки 38–41: обробляють клік на посилання [2]
- рядки 43–46: обробляють клік на посилання [3]
- рядки 58–61: обробляють клік на посилання [4]
- рядки 48–51: обробляють клік на посилання [5]
- рядки 53–56: обробляють клік на посилання [6]
Для виокремлення часто повторюваних фрагментів коду було створено два внутрішні методи:
- setVues, рядки 7–14: визначає, який вигляд або які види потрібно відобразити
- setMenu, рядки 16–21: визначає опції меню, які потрібно відобразити
8.6.2. Подія Load
Рекомендовані матеріали: довідка [1], Розробка WEB з ASP.NET 1.1:
- розділ «Компонент [Repeater] та зв’язок даних».
Першим, що бачить користувач, є порожня форма:

Для ініціалізації додатка необхідний доступ до джерела даних, який може завершитися невдачею. У цьому випадку першою сторінкою буде сторінка помилок:

Подія [Load] обробляється аналогічно до попередніх версій ASP.NET:
// завантаження сторінки
protected void Page_Load(object sender, System.EventArgs e)
{
// обробка початкового запиту
if (!IsPostBack)
{
// помилки ініціалізації?
if (Global.Erreur)
{
// відображення сторінки [erreurs]
...
// розміщення меню
...
return;
}
// завантаження імен співробітників у комбінований список
...
// розташування меню
...
// відображення виду [saisie]
...
}
}
Завдання: доповніть наведений вище код
8.6.3. Дія: Провести симуляцію
У наведеному нижче прикладі екран, позначений (1), — це запит користувача, а екран, позначений (2), — відповідь, яку йому надсилає веб-додаток. З головного екрана користувач може розпочати симуляцію:


![]() |
Процедура, яка обробляє цю дію, може виглядати наступним чином:
protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
{
// розрахунок заробітної плати
// сторінка дійсна?
Page.Validate();
if (!Page.IsValid)
{
// перегляд сторінки [saisie]
...
return;
}
// сторінка дійсна — дані, введені користувачем, збережено
double HeuresTravaillées = ...;
int JoursTravaillés = ...;
// розраховується заробітна плата працівника
FeuilleSalaire feuillesalaire;
try
{
feuillesalaire = ...
}
catch (PamException ex)
{
// відображення подання [erreurs]
...
return;
}
// результат зберігається в сесії
Session["simulation"] = ...
// виведення результатів
...
// відображення переглядів [saisie, employe, salaire]
...
// відображення меню
...
}
Завдання: доповніть наведений вище код
8.6.4. Дія: Записати симуляцію
Після завершення симуляції користувач може подати запит на її збереження:
![]() |

Процедура, що обробляє цю дію, може виглядати наступним чином:
protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
{
// поточне моделювання зберігається у списку моделювань, наявних у сесії
...
// відображення виду [simulations]
...
}
Завдання: доповніть наведений вище код
8.6.5. Дія: Повернення до форми симуляції
Рекомендовані матеріали: довідка [1], [Développement WEB avec ASP.NET 1.1 ]: роль прихованого поля _VIEWSTATE
Після відображення списку симуляцій користувач може повернутися до форми симуляції:
![]() |
Слід зауважити, що на екрані (2) форма відображається саме так, як її було заповнено. Тут слід пам’ятати, що ці різні види належать до однієї й тієї ж сторінки. Між різними запитами значення компонентів зберігаються за допомогою механізму ViewState, якщо ці компоненти мають властивість від EnableViewState до true.
Процедура, яка обробляє цю дію, може виглядати наступним чином:
protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
{
// відображення виду [saisie]
...
// розташування меню
...
}
Завдання: доповніть наведений вище код
8.6.6. Дія: Скинути симуляцію
Повернувшись до форми симуляції, користувач може подати запит на видалення наявних даних:
![]() |
Процедура, що обробляє цю дію, може виглядати наступним чином:
protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
{
// RAZ форми
...
}
Завдання: доповнити наведений вище код
8.6.7. Дія: Переглянути симуляції
Користувач може переглянути симуляції, які він уже виконав:
![]() |
![]() |
Процедура, що обробляє цю дію, може виглядати наступним чином:
protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
{
// отримання симуляцій у сесії
...
// чи є симуляції?
if (...)
{
// вигляд [simulations] видимий
...
}
else
{
// показ [SimulationsVides]
...
}
// зафіксовано меню
...
}
Завдання: доповніть наведений вище код
8.6.8. Дія: Видалити симуляцію
Користувач може подати запит на видалення симуляції:
![]() |
![]() |
Процедура [GridViewSimulations_RowDeleting ], яка обробляє цю дію, може виглядати наступним чином:
protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// завантажуємо симуляції в сесію
...
// видаляємо вказану симуляцію (e.RowIndex — номер видаленого рядка)
...
// чи залишилися ще симуляції?
if (...)
{
// заповнюється GridView
...
}
else
{
// перегляд [SimulationsVides]
...
}
}
Завдання: доповніть наведений вище код
8.6.9. Дія: Завершити сесію
Користувач може подати запит на завершення своєї сесії моделювання. Це призводить до скидання вмісту сесії та відображення порожньої форми:
![]() |
![]() |
Процедура, що обробляє цю дію, може виглядати наступним чином:
protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
{
// сесія завершується
...
// відобразити вікно [saisies]
...
// розташування меню
...
}
Завдання: доповніть наведений вище код
Практичне завдання: реалізуйте на комп’ютері попередній веб-додаток. Додайте до нього функціонал Ajax.






















