4. A aplicação [SimuPaie] – versão 1 – ASP.NET
4.1. Introdução
Pretendemos desenvolver uma aplicação .NET que permita ao utilizador simular cálculos de folhas de pagamento para prestadores de cuidados infantis na associação «Maison de la petite enfance» de um município.
O formulário de cálculo salarial ASP.NET terá o seguinte aspeto:

A aplicação ASP.NET terá a seguinte arquitetura:
![]() |
- Quando é efetuado o primeiro pedido à aplicação, é instanciado um objeto do tipo [Global], derivado do tipo [System.Web.HttpApplication]. Este objeto irá processar o ficheiro de configuração [web.config] da aplicação web e armazenar em cache determinados dados da base de dados (operação 0 acima).
- Quando a primeira solicitação (operação 1) é feita à página [Default.aspx], que é a única página da aplicação, o evento [Load] é tratado. É aqui que a caixa de combinação de funcionários é preenchida. Os dados necessários para a caixa de combinação são recuperados do objeto [Global], que os armazenou em cache. Esta é a operação 2 acima. A operação 4 envia a página inicializada ao utilizador.
- Quando a solicitação para calcular o salário (operação 1) é feita à página [Default.aspx], o evento [Load] é processado novamente. Nada é feito porque esta é uma solicitação POST, e o manipulador [Pam_Load] foi escrito para não fazer nada neste caso (usando o booleano IsPostBack). Assim que o evento [Load] é tratado, o evento [Click] no botão [Salary] é tratado a seguir. Este evento requer dados que não foram armazenados em cache em [Global]. Por conseguinte, o manipulador de eventos recupera-os da base de dados. Esta é a operação 3 acima. O salário é então calculado e a operação 4 envia os resultados ao utilizador.
4.2. O Visual Web Developer 2008
![]() |
- Em [1], crie um novo projeto
- em [2], do tipo [Web / Aplicação Web ASP.NET]
- Em [3], nomeie o projeto
- em [4], especifique o seu nome e em [5] a sua localização. Será criada uma pasta [c:\temp\pam-aspnet\pam-v1-adonet] para o projeto.
![]() |
- Em [5], o projeto do Visual Web Developer
- Em [6], as propriedades do projeto (clique com o botão direito do rato no projeto / Propriedades / Aplicação).
- em [7], o nome do assembly que será gerado quando o projeto for compilado
- em [8], o namespace padrão que queremos usar para as classes do projeto. A classe [_Default] definida nos ficheiros [Default.aspx.cs] e [Default.aspx.designer.cs] foi criada no namespace [pam_v1_adonet], derivado do nome do projeto. Pode alterar este namespace diretamente no código destes dois ficheiros:
[Default.aspx.cs]
using System;
....
namespace pam_v1
{
public partial class _Default : System.Web.UI.Page
{
[Default.aspx.designer.cs]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime version :2.0.50727.3603
//
// Changes made to this file may result in incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace pam_v1 {
public partial class _Default {
.........
A marcação do ficheiro [Default.aspx] também deve ser modificada:
![]() |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="pam_v1._Default" %>
...
O atributo Inherits acima refere-se à classe definida nos ficheiros [Default.aspx.cs] e [Default.aspx.designer.cs]. O namespace pam_v1 é utilizado nesses ficheiros.
4.2.1. O formulário [ Default.aspx]
A aparência visual do formulário [ Default.aspx] é a seguinte:
![]() |
Os componentes são os seguintes:
N.º | Tipo | Nome | Função |
Lista suspensa | EmployeeComboBox | Contém a lista de nomes dos funcionários | |
Caixa de Texto | Caixa de TextoHoras | Número de horas trabalhadas – número real | |
Caixa de Texto | TextBoxDays | Número de dias trabalhados – inteiro | |
Botão | BotãoSalário | Calcular salário | |
Caixa de texto | Caixa de texto de erro | Mensagem informativa para o utilizador ReadOnly=true, TextMode=MultiLine | |
Rótulo | LabelName | Nome do funcionário selecionado em (1) | |
Rótulo | NomePróprio | Nome próprio do funcionário selecionado em (1) | |
Rótulo | RótuloEndereço | Endereço | |
Etiqueta | Cidade | Cidade | |
Etiqueta | Código postal | Código postal | |
Etiqueta | Índice de etiquetas | Índice | |
Etiqueta | RótuloCSGRDS | Taxa de contribuição CSGRDS | |
Etiqueta | Rótulo CSGD | Taxa de contribuição CSGD | |
Etiqueta | Etiqueta de reforma | Taxa de contribuição para a reforma | |
Rótulo | RótuloSS | Taxa de contribuição para a Segurança Social | |
Rótulo | LabelSH | Salário-base por hora para o índice indicado em (11) | |
Etiqueta | EtiquetaEJ | Subsídio de subsistência diário para o índice indicado em (11) | |
Etiqueta | LabelRJ | Subsídio diário para refeições para o índice indicado em (11) | |
Etiqueta | LabelVacation | Taxa de subsídio de férias a aplicar ao salário base | |
Etiqueta | LabelSB | Montante do salário base | |
Etiqueta | RótuloCS | Montante das contribuições para a segurança social a pagar | |
Rótulo | RótuloIE | Montante do abono de família | |
Rótulo | LabelIR | Montante dos subsídios de alimentação para a criança em acolhimento | |
Etiqueta | LabelSN | Salário líquido a pagar ao funcionário |
O formulário também contém dois contentores [Panel]:
contém o componente (5) TextBoxError | |
contém os componentes (6) a (24) |
Um componente [Panel] pode ser tornado visível ou ocultado programaticamente utilizando a sua propriedade booleana [Panel].Visible.
4.2.2. Validação de entrada
Para calcular um salário, o utilizador:
- seleciona um funcionário em [1]
- introduz o número de horas trabalhadas em [2]. Este número pode ser decimal, como 2,5 para 2 horas e 30 minutos.
- introduz o número de dias trabalhados em [3]. Este número é um número inteiro.
- calcula o salário utilizando o botão [4]
![]() |
Quando o utilizador introduz dados incorretos em [2] e [3], estes são validados assim que o utilizador muda de campo de entrada. Assim, a captura de ecrã abaixo foi tirada antes mesmo de o utilizador clicar no botão [Salário]:
![]() |
São necessárias duas condições para o comportamento descrito acima:
- os componentes de validação devem ter a propriedade EnableClientScript definida como true:
![]() |
- O navegador que exibe a página deve ser capaz de executar o código JavaScript incorporado numa página HTML.
Se o navegador do cliente não verificar a validade dos dados por si próprio, os dados só serão verificados quando o navegador enviar os dados do formulário para o servidor. Será então o código no servidor que processa o pedido do navegador que verificará a validade dos dados. Note que esta validação deve ser sempre realizada, mesmo que a página apresentada no navegador do cliente contenha código JavaScript que execute a mesma validação. Isto porque o servidor não pode ter a certeza de que a solicitação POST que recebe provém efetivamente dessa página e que, por conseguinte, os dados foram validados.
A lista de validadores é a seguinte:
N.º | Tipo | Nome | Função |
Validador de campo obrigatório | Validador de campo obrigatório Horas | verifica se o campo [2] [TextBoxHeures] não está vazio | |
RangeValidator | RangeValidatorHours | verifica se o campo [2] [TextBoxHours] é um número real no intervalo [0, 200] | |
Validador de Campo Obrigatório | RequiredFieldValidatorDays | verifica se o campo [3] [TextBoxDays] não está vazio | |
RangeValidator | RangeValidatorDays | verifica se o campo [3] [TextBoxDays] é um número inteiro no intervalo [0,31] |
Tarefa: Crie a página [Default.aspx]. Primeiro, coloque os dois contentores [PanelErrors] e [PanelSalary] para que possa, em seguida, colocar os componentes que eles devem conter.
4.2.3. Entidades da aplicação
![]() |
Depois de lidas, as linhas das tabelas [contributions], [employees] e [allowances] serão armazenadas em objetos dos tipos [Contributions], [Employee] e [Allowances], definidos da seguinte forma:
namespace Pam.Entites
{
public class Cotisations
{
// automatic properties
public double CsgRds { get; set; }
public double Csgd { get; set; }
public double Secu { get; set; }
public double Retraite { get; set; }
// manufacturers
public Cotisations()
{
}
public Cotisations(double csgRds, double csgd, double secu, double retraite)
{
CsgRds = csgRds;
Csgd = csgd;
Secu = secu;
Retraite = retraite;
}
// ToString
public override string ToString()
{
return string.Format("[{0},{1},{2},{3}]", CsgRds, Csgd, Secu, Retraite);
}
}
}
namespace Pam.Entites
{
public class Employe
{
// automatic properties
public string SS { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Adresse { get; set; }
private string Ville { get; set; }
private string CodePostal { get; set; }
private int Indice { get; set; }
// manufacturers
public Employe()
{
}
public Employe(string ss, string nom, string prenom, string adresse, string codePostal, string ville, int indice)
{
SS = ss;
Nom = nom;
Prenom = prenom;
Adresse = adresse;
CodePostal = codePostal;
Ville = ville;
Indice = indice;
}
// ToString
public override string ToString()
{
return string.Format("[{0},{1},{2},{3},{4},{5},{6}]", SS, Nom, Prenom, Adresse, Ville, CodePostal, Indice);
}
}
}
namespace Pam.Entites
{
public class Indemnites
{
// automatic properties
public int Indice { get; set; }
public double BaseHeure { get; set; }
public double EntretienJour { get; set; }
public double RepasJour { get; set; }
public double IndemnitesCP { get; set; }
// manufacturers
public Indemnites()
{
}
public Indemnites(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP)
{
Indice = indice;
BaseHeure = baseHeure;
EntretienJour = entretienJour;
RepasJour = repasJour;
IndemnitesCP = indemnitesCP;
}
// identity
public override string ToString()
{
return string.Format("[{0}, {1}, {2}, {3}, {4}]", Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCP);
}
}
}
4.2.4. Configuração da aplicação
O ficheiro [Web.config] que configura a aplicação será o seguinte:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<connectionStrings>
<add name="dbpamSqlServer2005" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=C:\data\...\dbpam.mdf;User Id=sa;Password=msde;Connect Timeout=30;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<add key="selectEmploye" value="select NOM,PRENOM,ADRESSE,VILLE,CODEPOSTAL,INDICE from EMPLOYES where SS=@SS"/>
<add key="selectEmployes" value="select PRENOM, NOM, SS from EMPLOYES"/>
<add key="selectCotisations" value="select CSGRDS,CSGD,SECU,RETRAITE from COTISATIONS"/>
<add key="SelectIndemnites" value="select INDICE,BASEHEURE,ENTRETIENJOUR,REPASJOUR,INDEMNITESCP from INDEMNITES"/>
</appSettings>
<system.web>
<!--
Définissez compilation debug="true" pour insérer des symboles
de débogage dans la page compilée. Comme ceci
affecte les performances, définissez cette valeur à true uniquement
lors du développement.
-->
<compilation debug="false">
...
</configuration>
- linha 9: define a cadeia de ligação à base de dados SQL Server
- linhas 13–16: definem as consultas SQL utilizadas pela aplicação para evitar a sua codificação rígida no código.
- linha 13: a consulta SQL é parametrizada. A notação dos parâmetros é específica do SQL Server.
Tarefa: Introduza estes parâmetros no ficheiro [Web.config]. A linha 9 será adaptada ao caminho real da base de dados [dbpam.mdf].
4.2.5. Inicialização da aplicação
Uma aplicação ASP.NET é inicializada pelo ficheiro [Global.asax.cs]. Está estruturada da seguinte forma:
![]() |
- Em [1], adicione um novo item ao projeto
- Em [2], adicione a classe global da aplicação, que se chama [Global.asax] por predefinição [3]
![]() |
- em [4], o ficheiro [Global.asax] e a classe associada [Global.asax.cs]
- Em [5], exiba a marcação para [Global.asax]
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v1.Global" Language="C#" %>
A classe [pam_v1.Global] está definida no ficheiro [Global.asax.cs]. Para os nossos fins, será definida da seguinte forma:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using Pam.Entites;
namespace pam_v1
{
public class Global : System.Web.HttpApplication
{
// --- static application data ---
public static Employe[] Employes;
public static Cotisations Cotisations;
public static Dictionary<int, Indemnites> Indemnites = new Dictionary<int, Indemnites>();
public static string Msg = string.Empty;
public static bool Erreur = false;
public static string ConnectionString = null;
// application startup
public void Application_Start(object sender, EventArgs e)
{
...
try
{
// connection
ConnectionString = ...
using (SqlConnection connexion = new SqlConnection(ConnectionString))
{
connexion.Open();
// retrieve the list of employees and place it in the static array [Employees]
...
// contribution rates are retrieved from the static variable [Contributions]
...
// retrieve allowances from the static dictionary [Allowances]
...
// we succeeded
Msg = "Base chargée...";
}
}
catch (Exception ex)
{
// we note the error
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
}
}
- Linha 20: O método [Application_Start] é executado quando a aplicação web é iniciada. É executado apenas uma vez.
- linhas 12–17: campos públicos e estáticos da classe. Um campo estático é partilhado por todas as instâncias da classe. Assim, se forem criadas várias instâncias da classe [Global], todas elas partilham o mesmo campo estático [Employees], acessível através da referência [Global.Employees], ou seja, [ClassName].StaticField. [Global] é o nome de uma classe. É, portanto, um tipo de dados. Este nome é arbitrário. A classe deriva sempre de [System.Web.HttpApplication].
Voltemos à nossa classe [Global]. Poder-se-á questionar se é necessário declarar os seus campos como estáticos. De facto, parece que, em alguns casos, pode haver várias instâncias da classe [Global], o que justifica tornar estáticos os campos que precisam de ser partilhados por todas essas instâncias.
Existe outra forma de partilhar dados do âmbito «aplicação» entre as diferentes páginas de uma aplicação web. Assim, a matriz Employees poderia ser armazenada no procedimento Application_Start da seguinte forma:
Application.Add("employes",Employes);
Por predefinição, [Application] em qualquer aplicação ASP.NET é uma referência a uma instância da classe definida em [Global.asax.cs]. Application é um contentor capaz de armazenar objetos de qualquer tipo. A matriz Employees poderia então ser recuperada no código de qualquer página da aplicação web da seguinte forma:
Employe[] employes=Application.Get["employes"] as Employe[];
Como o contentor Application armazena objetos de qualquer tipo, recuperamos um tipo Object que tem de ser convertido. Este método é sempre utilizável, mas a partilha de dados através de campos estáticos tipados do objeto [Global] evita a conversão e permite que o compilador realize verificações de tipo que auxiliam o programador. Este é o método que será utilizado aqui.
Os dados partilhados por todos os utilizadores são os seguintes:
- linha 12: a matriz de objetos [Employee] que armazenará a lista simplificada (SSN, LAST_NAME, FIRST_NAME) de todos os funcionários
- linha 13: o objeto do tipo [Contributions] que armazenará as taxas de contribuição
- linha 14: o dicionário que armazenará os subsídios associados aos vários índices dos funcionários. Será indexado pelo índice do funcionário e os seus valores serão do tipo [Subsídios]
- linha 15: uma mensagem indicando se a inicialização foi concluída com sucesso ou com um erro
- linha 16: um valor booleano indicando se a inicialização terminou com um erro ou não
- linha 17: a cadeia de ligação à base de dados.
Pergunta: Complete o código para a classe [Application_Start].
4.3. Eventos do formulário [Default.aspx]
4.3.1. O procedimento [P age_Load]
Quando o formulário [Default.aspx] é carregado, ele recupera os nomes dos funcionários da matriz [Global.Employees] (linha 12) e preenche a lista suspensa [1] com esses nomes:
![]() |
- A lista suspensa [1] foi preenchida
- A caixa de texto [5] indica que a base de dados foi lida corretamente
Se ocorreram erros de inicialização quando a aplicação foi iniciada, a caixa de texto [5] indica isso:

Pergunta: Escreva o procedimento [Page_Load] para a página web [Default.aspx], que, quando executado no arranque da aplicação, garante o comportamento descrito acima.
4.3.2. Cálculo do salário
Clicar no botão [4] aciona o manipulador:
protected void ButtonSalaire_Click(object sender, System.EventArgs e)
Este manipulador começa por verificar a validade das entradas feitas em [2] e [3]. Se alguma delas estiver incorreta, o erro é reportado conforme mostrado anteriormente. Assim que as entradas em [2] e [3] forem verificadas e consideradas válidas, a aplicação deve apresentar dados adicionais sobre o utilizador selecionado em [1], bem como o seu salário (ver captura de ecrã na secção 4.1).
Pergunta: Escreva o código para o procedimento [ButtonSalaire_Click].











