Frédéric Mélantois
Contrôles ASP.NET 2.0, l'utilisation des régions
Par Frédéric Mélantois publié le 22/08/2006 à 19:46, lu 5243 fois, 4 pages
 3 | La partie Design du contrôle
La partie Design du contrôle
En élaborant un « Designer » associé à notre contrôle ASP.NET nouvellement créé, nous allons offrir aux développeurs utilisant celui-ci dans Visual Studio 2005, une ergonomie personnalisée, en particulier, la possibilité de sélectionner des régions dans notre contrôle. Ces régions peuvent être éditables ou ne pas l'être, offrir un smart-tag de configuration ou non. Je vous laisse imaginer toutes les élaborations possibles.

Afin de faciliter la compréhension, notre exemple restera très simple et purement didactique. Créons le squelette de ce « designer », en définissant les régions :
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.UI;
using System.Web.UI.Design.WebControls;
using System.Web.UI.Design;

public class WebCartoucheDesigner : CompositeControlDesigner
{
        
    private WebCartouche _webCartouche;
    private int _nbRegions = 0;

    public override void Initialize(IComponent component)
    {
        _webCartouche = (WebCartouche)component;
        base.Initialize(component);
    }

    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        if (_webCartouche.regions != null)
        {
            _nbRegions = _webCartouche.regions.Count;
            for (int cpt = 0; cpt < _nbRegions; cpt++)
            {
                _webCartouche.regions[cpt].SetAttribute(DesignerRegion.DesignerRegionAttributeName, cpt.ToString());
            }
        }
    }
}
De manière classique, nous récupérons une instance du composant « hosté » par le designer.

Lors de la création des contrôles enfants, nous ajoutons un attribut particulier aux contrôles qui sont succeptibles d'accueillir une région dans le designer : La collection « regions » de notre contrôle WebCartouche précédemment construit, contient dans l'ordre la liste des contrôles pouvant accueillir une région. C'est en récupérant cette liste dans WebCartoucheDesigner, que nous pouvons ajouter un attribut à ses contrôles : l'attribut « DesignerRegion ».

Vous allez me dire qu'on aurait pu directement ajouter cet attribut aux contrôles de WebCartouche, vous aurez parfaitement raison. Mais il m'apparaît plus intéressant de se découpler d'un nom de région entre WebCartouche et WebCartoucheDesigner (nom qu'il faut faire correspondre !). C'est pourquoi, je trouve plus judicieux d'utiliser l'ordre d'enregistrement des régions.
Un autre argument en faveur de cette façon de procéder est la mise en oeuvre d'un nombre indéterminé de régions, « Indéterminé » du moins, lors de la création du contrôle. L'utilisateur (le développeur utilisateur du contrôle) peut fixer le nombre de régions via une propriété par exemple.

L'ajout de cet attribut permet au parseur du designer d'identifier une zone du contrôle que l'on souhaite sélectionner.
private int _currentRegion = -1;

protected override void OnClick(DesignerRegionMouseEventArgs e)
{
    base.OnClick(e);
    _currentRegion = -1;
    if (e.Region != null)
    {
        for (int cpt = 0; cpt < _nbRegions; cpt++)
        {
            if (e.Region.Name == cpt.ToString())
                _currentRegion = cpt;
        }
        UpdateDesignTimeHtml();
    }    
}
Un nouvel événement est apparu avec la version 2.0 de ASP.NET, il permet de gérer le click de souris dans le designer. Nous sommes donc en mesure d'identifier la région du contrôle où le click a eu lieu. Ici, comme vu précédemment, le nom de région correspond au numéro d'apparition du contrôle en question. Nous stockons dans une variable le numéro de région et nous provoquons par la méthode « UpdateDesignTimeHtml() » la mise à jour du designer. Par le clic de souris, nous devons être en mesure de mettre en avant la région sélectionnée. Pour cela, il nous suffit de surcharger la méthode « GetDesignTimeHtml » en ayant pris soin d'ajouter de nouveaux « flags » lors de l'initialisation du composant :
public override void Initialize(IComponent component)
{
    _webCartouche = (WebCartouche)component;
    base.Initialize(component);
    //evite de faire appel manuellement a la methode CreateChildControls() pour le designTimeHtml
    SetViewFlags(ViewFlags.DesignTimeHtmlRequiresLoadComplete,true);
    //autorise l'edition de template.
    SetViewFlags(ViewFlags.TemplateEditing, true); 
}
Voici la surcharge de la méthode « GetDesignTimeHtml » :
public override string GetDesignTimeHtml(DesignerRegionCollection regions)
{
    //necessaire pour initialiser la variable _nbRegions utilisee ci-dessous.
    this.CreateChildControls(); 
            
    for (int cpt = 0; cpt < _nbRegions; cpt++)
    {
        DesignerRegion r;
        if (_currentRegion == cpt) 
            r = new EditableDesignerRegion(this, cpt.ToString());
        else
            r = new DesignerRegion(this, cpt.ToString());
        regions.Add(r);
    }

    if (_currentRegion >= 0 && _currentRegion < _nbRegions)
        regions[_currentRegion].Highlight = true;
    return base.GetDesignTimeHtml(regions);
}
En cliquant sur la région, on peut rendre éditable la région (par instanciation de EditableDesignerRegion), ce n'est pas une obligation (on peut par exemple simplement offrir un smart-tag à l'utilisateur pour paramétrer la zone, dans ce cas-là, il devient suffisant d'instancier DesignerRegion). On met en avant la région cliquée par la propriété « Highlight ».

Si nous décidons de rendre éditable une zone, il nous suffit de surcharger les méthodes « GetEditableDesignerRegionContent » et « SetEditableDesignerRegionContent » de la façon suivante :
public override string GetEditableDesignerRegionContent(EditableDesignerRegion region)
{
    IDesignerHost host = (IDesignerHost)Component.Site.GetService(typeof(IDesignerHost));
    if (host != null)
    {
        ITemplate contentTemplate;
        if (_currentRegion == 1)
        {
            contentTemplate = _webCartouche.ContentTemplate;
            return ControlPersister.PersistTemplate(contentTemplate, host);
        }

        ITemplate titleTemplate;
        if (_currentRegion == 0)
        {
            titleTemplate = _webCartouche.TitleTemplate;
            return ControlPersister.PersistTemplate(titleTemplate, host);
        }
    }
    return String.Empty;
}
On récupère une instance du designer hébergeant les composants et implémentant IdesignerHost. Cette dernière interface offre un grand nombre de propriétés et de services pour gérer les composants dans le designer. Par exemple, la liste des composants est donné par host.Container.Components.

Le designer hébergeant notre composant est doté d'un mécanisme de persistance. Nous allons donc pouvoir faire persister le contenu des templates de WebCartouche. La classe « ControlPersister » nous aide grandement dans cette tâche.
L'attribut que nous avons défini dans notre contrôle, « PersistenceModeAttribute », va permettre de préciser le mode de persistance. Au final, les templates persistent sous forme de « string ».

Le code de la surcharge de la méthode « SetEditableDesignerRegionContent » est le suivant :
public override void SetEditableDesignerRegionContent(EditableDesignerRegion region, string content)
{
    if (content == null)
        return;

    IDesignerHost host = (IDesignerHost)Component.Site.GetService(typeof(IDesignerHost));

    if (host != null)
    {

        ITemplate template = ControlParser.ParseTemplate(host, content);

        if (template != null)
        {
            if (_currentRegion == 0)
            {
                _webCartouche.TitleTemplate = template;
            }

            if (_currentRegion == 1)
            {
                _webCartouche.ContentTemplate = template;
            }
                                            
        }
    }
}
Comme précédemment, c'est en récupérant une instance du designer hébergeant notre composant, que l'on va pouvoir utiliser les services permettant de le manipuler.

A partir des éléments persistants, on est en mesure de parser le contenu grâce à la classe « ControlParser ». En fonction de la zone courante, on peut impacter les changements dans le template.
 
» Démarrer une discussion