Communication entre les différents éléments d'une page ASP.NET

Cet article vous présente comment faire communiquer les différents éléments d'une page ASP.NET. On y verra comment faire communiquer une ContentPage avec sa MasterPage, ainsi que comment faire communiquer un UserControl avec sa page mère.

N'hésitez pas à commenter cet article ! Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Introduction

A travers cet article, vous allez voir comment faire communiquer une ContentPage avec sa MasterPage. Puis vous verrez comment faire communiquer un UserControl avec sa page mère.

Le site web utilisé dans l'article est développé en C# / ASP.NET avec Visual Studio 2008, et se nomme MasterPageContentPageUserControl.
La structure de la MasterPage, nommée DvpMasterPage, est la suivante:

Structure de la MasterPage

Le menu haut contiendra les liens permettant de naviguer entre les différentes pages du site.
Le menu bas contiendra un bouton et une liste déroulante.
Le pied de page contiendra un label.
Le code source est disponible à la fin de cet article.

I. Communication entre une MasterPage et une ContentPage

I-A. Communication de la ContentPage vers la MasterPage

Comme évoqué plus haut, le pied de page de la MasterPage contient un label nommé lblFooter, dont le texte par défaut est "Pied de page par défaut":

 
Sélectionnez
<div id="footer">
	<asp:Label ID="lblFooter" runat="server">Footer par défaut</asp:Label>
</div>
				

Le but ici est de pouvoir modifier le texte du label à partir de la ContentPage.
On va imaginer le fonctionnement suivant:
- Une textbox et un bouton dans la ContentPage. Un clic sur le bouton entraîne une mise à jour du texte du pied de page avec le texte saisi dans la textbox.

Code aspx:

 
Sélectionnez
Modifier le texte du footer: <asp:TextBox ID="tbFooter" runat="server"></asp:TextBox>
&nbsp;
<asp:Button id="btnSubmit" runat="server" Text="Valider" OnClick="btnSubmit_Click"/>

I-A-1. La méthode "pas propre"

La 1ère idée qui vient à l'esprit est d'utiliser la méthode FindControl de la classe MasterPage, de caster l'objet Control récupéré en Label puis d'assigner une valeur à sa propriété Text.
Cela donnerait:

 
Sélectionnez
protected void btnSubmit_Click(object sender, EventArgs e)
{
	// On récupère le label du pied de page
	var footer = (Label)this.Master.FindControl("lblFooter");
	footer.Text = tbFooter.Text;
}

Cela fonctionne mais ce n'est pas vraiment idéal. Si vous vous trompez dans le nom du label passé à la fonction FindControl, ou si quelques temps plus tard vous modifiez le nom du label dans la MasterPage, vous aurez droit à une belle erreur lors de l'exécution du site web, alors que tout compile correctement.

I-A-2. La méthode "propre"

L'idée ici est de définir, au niveau de la MasterPage, une propriété qui mettra à jour le texte du label du pied de page.
Dans notre cas, une propriété SetOnly suffit. Ce qui donne:

 
Sélectionnez
public string FooterText
{
	set
	{
		lblFooter.Text = value;
	}
}

L'étape suivante est d'accéder à cette propriété depuis la ContentPage.
Ce qui donnerait:

 
Sélectionnez
protected void btnSubmit_Click(object sender, EventArgs e)
{
    this.Master.FooterText = tbFooter.Text;
}

Le problème est que ce code ne compile pas. D'ailleurs l'IntelliSense ne propose pas la propriété FooterText.

Image non disponible

La raison est simple: this.Master retourne un objet de type MasterPage, et non un objet de type DvpMasterPage.

Image non disponible

Une solution serait de caster cet objet en DvpMasterPage. Ce qui donnerait:

 
Sélectionnez
protected void btnSubmit_Click(object sender, EventArgs e)
{
    ((DvpMasterPage) this.Master).FooterText = tbFooter.Text;
}

Cela fonctionne et, maintenant, l'IntelliSense nous propose la propriété FooterText.
Il existe cependant une solution plus élégante: la directive MasterType
Cette directive permet de fortement typer notre MasterPage. Au niveau du code aspx de notre page, cela donne:

 
Sélectionnez
<%@ MasterType VirtualPath="~/DvpMasterPage.Master" %>

A partir de maintenant, la propriété Master de la ContentPage est fortement typée comme vous pouvez le voir:

Image non disponible

Et la propriété FooterText est directement accessible. Cela donne finalement:

 
Sélectionnez
protected void btnSubmit_Click(object sender, EventArgs e)
{
    this.Master.FooterText = tbFooter.Text;
}

Pour info, le fait d'insérer la directive MasterType surcharge la propriété Master dans le fichier designer.cs de la ContentPage, d'où le typage fort:

 
Sélectionnez
/// <summary>
/// Master property.
/// </summary>
/// <remarks>
/// Auto-generated property.
/// </remarks>
public new DVP.MasterPageContentPageUserControl.DvpMasterPage Master {
    get {
        return ((DVP.MasterPageContentPageUserControl.DvpMasterPage)(base.Master));
    }
}

I-B. Communication de la MasterPage vers la ContentPage

Le but ici est de récupérer dans la ContentPage, un clic sur le bouton ou une sélection dans la liste déroulante de la MasterPage.

Le principe est le suivant:
  • Du côté de la MasterPage: déclarer des événements et les lever lors du clic sur le bouton ou lors de la sélection d'un élément dans la liste déroulante
  • Du côté de la ContentPage: s'abonner aux événements de la MasterPage

I-B-1. Côté MasterPage

On va déclarer 2 événements: un pour le clic sur le bouton et un pour la sélection d'un élément dans la liste déroulante.
Cela donne:

 
Sélectionnez
public event EventHandler ColorChanged;
public event EventHandler BoutonClicked;

Il reste maintenant à lever ces événements au moment adéquat:

 
Sélectionnez
protected void btnMenu_Click(object sender, EventArgs e)
{
    if (BoutonClicked != null)
        BoutonClicked(sender, e);
}

protected void ddlColors_SelectedIndexChanged(object sender, EventArgs e)
{
    if (ColorChanged != null)
        ColorChanged(sender, e);
}

On va également avoir besoin d'une propriété qui permet de récupérer l'élément sélectionné dans la liste déroulante:

 
Sélectionnez
public string SelectedColor
{
    get
    {
        return ddlColors.SelectedValue;
    }
}

I-B-2. Côté ContentPage

Il faut commencer par créer les gestionnaires d'événements ("Event Handler"). Un gestionnaire d'événement est la méthode qui va s'exécuter en réponse à un événément.

 
Sélectionnez
private void Master_BoutonClicked(object sender, EventArgs e)
{
    label.Text = string.Format("Vous avez cliqué sur le bouton de la MasterPage à {0}", DateTime.Now.ToLongTimeString());
}

private void Master_ColorChanged(object sender, EventArgs e)
{
    label.Text = "Vous avez sélectionné la couleur " + this.Master.SelectedColor;
}

Il faut ensuite s'abonner à ces événements lors du chargement de la page. C'est là qu'interviennent les gestionnaires d'événéments définis précédemment:

 
Sélectionnez
protected void Page_Load(object sender, EventArgs e)
{
    this.Master.ColorChanged += Master_ColorChanged;
    this.Master.BoutonClicked += Master_BoutonClicked;
}

On peut voir que l'IntelliSense propose les événements définis dans la MasterPage

Image non disponible

Dorénavant, la ContentPage est automatiquement "avertie" de toute action sur le bouton ou la liste déroulante contenus dans la MasterPage.

II. Communication entre une Page et un UserControl

On va imaginer le scénario suivant:
  • Un UserControl qui contient un contrôle Calendar
  • Une page aspx qui contient ce UserControl
  • A partir de la page, on souhaite pouvoir spécifier le mois à afficher dans le Calendar
  • On souhaite récupérer au niveau de la page la date sélectionnée par l'utilisateur dans le Calendar

II-A. Le UserControl

Côté design, le UserControl comporte uniquement un contrôle Calendar:

 
Sélectionnez
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UcCalendar.ascx.cs" Inherits="DVP.MasterPageContentPageUserControl.UcCalendar" %>
<asp:Calendar ID="Calendar1" runat="server" onselectionchanged="Calendar1_SelectionChanged"></asp:Calendar>

Il faut créer une propriété qui sera utilisée pour spécifier le mois à afficher. Lorsque cette propriété sera settée, on affichera également le Calendar qui est invisible par défaut:

 
Sélectionnez
public Constants.Months Month
{
    set
    {
        Calendar1.VisibleDate = new DateTime(DateTime.Now.Year, (int)value, 1);
        Calendar1.Visible = true;
    }
}

Le type Months est enum définie dans une classe static:

 
Sélectionnez
public static class Constants
{
    public enum Months
    {
        Janvier = 1,
        Fevrier = 2,
        Mars = 3,
        Avril = 4,
        Mai = 5,
        Juin = 6,
        Juillet = 7,
        Aout = 8,
        Septembre = 9,
        Octobre = 10,
        Novembre = 11,
        Decembre = 12
    }
}

Il faut également créer une propriété qui retournera la date sélectionnée dans le Calendar:

 
Sélectionnez
public DateTime SelectedDate
{
    get
    {
        return Calendar1.SelectedDate;
    }
}

La dernière étape consiste à mettre en place le mécanisme qui a pour but d'informer la page qui contient le UserControl lorsque l'utilisateur sélectionne une date dans le Calendar.

Une solution serait d'utiliser la méthode FindControl afin de récupérer le Label puis d'assigner une valeur à sa propriété Text. Cela donnerait:

 
Sélectionnez

protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
	((Label)this.Parent.FindControl("lblSelectedDate")).Text = string.Format("La date sélectionnée est {0}", Calendar1.SelectedDate.ToLongDateString());
}

Encore une fois, cette solution est peu élégante. D'une part, on n'est pas à l'abri d'une faute de frappe sur le nom du Label ou d'un renommage de ce dernier ultérieurement. D'autre part, cela lie fortement le UserControl à la page, ce qui va à l'encontre du but d'un UserControl. En effet, un UserControl a pour but d'être réutilisé à différents endroits. Que se passera-t-il si vous utilisez ce UserControl dans une page qui ne contient pas de label intitulé lblSelectedDate? Vous aurez droit à une belle exception à l'exécution.

La bonne méthode est de créer un événement que l'on lévera lors de la sélection d'une date dans le Calendar et qui sera intercepté par la page.

 
Sélectionnez
public event EventHandler SelectedDateChanged;

protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
    if (SelectedDateChanged != null)
        SelectedDateChanged(sender, e);
}

II-B. La Page

Dans notre exemple, la page qui contient le UserControl est très simple. Outre le UserControl, on y trouve également:
- une liste déroulante contenant la liste des mois
- un Label qui affichera la date sélectionnée dans le Calendar du UserControl.

La liste déroulante a sa propriété AutoPostBack à true. Ainsi, chaque sélection d'un élément entrainera automatiquement une mise à jour du Calendar.

 
Sélectionnez
<asp:DropDownList 
    ID="ddlMonths" 
    runat="server" 
    AutoPostBack="true" 
    onselectedindexchanged="ddlMonths_SelectedIndexChanged">
</asp:DropDownList>

La liste déroulante est databindée au Page_Load à partir des valeurs de l'enumération Months précédemment définie.

 
Sélectionnez
protected void Page_Load(object sender, EventArgs e)
{
    if(!IsPostBack)
    {
        ddlMonths.DataSource = Enum.GetNames(typeof(Constants.Months));
        ddlMonths.DataBind();
    }
}

Lors de la sélection d'un élément dans la liste déroulante, et afin de mettre à jour le Calendar, il suffit d'attribuer une valeur à sa propriété Month. Ce qui donne:

 
Sélectionnez
protected void ddlMonths_SelectedIndexChanged(object sender, EventArgs e)
{
    var selectedMonth = (Constants.Months)Enum.Parse(typeof(Constants.Months), ddlMonths.SelectedValue);
    UcCalendar1.Month = selectedMonth;
}

La dernière étape est de récupérer la date sélectionnée dans le Calendar et de l'afficher dans un Label. Pour cela, il suffit de s'abonner à l'événement levé par le UserControl lors de la sélection d'une date.
Avant de s'abonner à l'événement, il faut créer le gestionnaire d'événement, cette méthode qui sera appelée en réponse à un événement. Ce gestionnaire aura la simple tâche d'afficher la date sélectionnée dans le Calendar. Pour cela, il suffit d'utiliser la propriété du UserControl définie dans ce but. Cela donne:

 
Sélectionnez
protected void UcCalendar1_SelectedDateChanged(object sender, EventArgs e)
{
    lblSelectedDate.Text = string.Format("La date sélectionnée est {0}", UcCalendar1.SelectedDate.ToLongDateString()); 
}

Il y a 2 façons de s'abonner à cet événement: de façon déclarative ou de façon programmatique.

De façon programmatique dans le Page_Load de la page:

 
Sélectionnez
protected void Page_Load(object sender, EventArgs e)
{
    UcCalendar1.SelectedDateChanged += new EventHandler(UcCalendar1_SelectedDateChanged);
}

Ou alors de façon déclarative dans le code aspx:

 
Sélectionnez
<uc1:UcCalendar ID="UcCalendar1" runat="server" OnSelectedDateChanged="UcCalendar1_SelectedDateChanged" />

Notez qu'il faut ajouter le nom de l'événement avec le préfixe "On" lors de la déclaration de façon déclarative.

Conclusion

Cet article vous a montré comment faire communiquer une MasterPage avec une ContentPage, ainsi qu'un UserControl avec une Page.

Liens

Remerciements

Je remercie l'équipe Dotnet pour leurs relectures attentives du document.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Pascal ROZE. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.