lundi 15 novembre 2010

Référencer en CSS 2.0 des éléments sans id ni classe

Un de mes collègues m’a montré un cas assez spécial aujourd’hui dans SharePoint. Il a construit un formulaire de saisie dans SharePoint avec l’objet InputFormSection qui est un Template User Control. Bref en résumé cet objet génère en html un objet avec un tag <tr> et des <td> pour chacune des sections.

Le problème est survenu lorsqu’il a ajouté le contrôle PeoplePicker de SharePoint dans une des sections. Le code html généré par le PeoplePicker crée automatiquement un espace à droite de 10 pixels ainsi qu’une ligne en dessous. Ce qui n’était pas très élégant pour l’affichage du formulaire. Le défi est donc de cacher la cellule de 10 pixels ainsi que la ligne qui a été rajoutée en dessous du contrôle PeoplePicker.

Le PeoplePicker est un WebControl, ce qui veut dire qu’il est compilé dans un assembly, donc pas facilement modifiable.
Après réflexion je ne vois que deux solutions pour cacher ces deux éléments : soit via CSS ou Javascript.

J’ai donc choisi la première solution.

Pour rajouté à la tâche, les deux éléments n’ont aucun ID ni aucune class CSS. Ils sont donc assez difficiles à référencer.
Voici un exemple de code, différent de l’original, mais qui démontre bien la situation.



<table id="tblMyTable">
<tr class="myTrClass">
<td class="myTDClass1">Data 1</td>
<td class="myTDClass1">Data 2</td>
<td width="10">&nbsp;</td> <%--To be removed--%>
</tr>
<tr> <%--To be removed--%>
<td> </td>
<td><img src="Line.jpg" height="5" width="200"/></td>
<td> </td>
</tr>
</table>


Je cherchais un moyen de référencer le 3e tag TD afin de lui appliquer le style « Display: none; ». J’ai donc discuté mon collègue Sébastien qui connait bien le CSS. Il m’a donc parlé du symbole « + » en CSS 2.0 qui permet de référencer l’objet qui succède directement à l’élément précédent.
J’ai donc créé le style suivant dans le tag « Head » de la page afin de cacher le tag TD :


<style type="text/css">
#tblMyTable .myTDClass1 + .myTDClass1 + td
{
display: none;
}
</style>

Le code ci-dessus sélectionne le tag avec la classe « myTDClass1 » dans la table dont le id est « tblMyTable » (1er TD), ensuite il sélectionne l’objet qui lui succède directement et qui a également la classe « myTDClass1 » (2e TD) et ensuite le TD qui lui succède directement c'est-à-dire le 3e TD.

Même principe pour cacher le 2e TR.


<style type="text/css">
#tblMyTable .myTrClass + tr
{
display: none;
}
</style>

En résumé, vive le CSS!

dimanche 14 novembre 2010

Utilisation du mot-clé yield pour palier aux variables de retour temporaire

Il vous est surement arrivé de devoir retourner un IEnumerable dans une de vos méthodes. La façon classique, est de se déclarer une variable temporaire afin de pouvoir ajouter le contenu désiré et ensuite retourner la collection.

Voici un exemple qui affiche les nombres pairs entre dans un intervalle donné:


public IEnumerable<int> GetPairNumber(int From, int To)
{
List<int> retVal = new List<int>();


for (int indexNumber = From; indexNumber < To; indexNumber++)
{
if (indexNumber % 2 == 0)
{
retVal.Add(indexNumber);
}
}
return retVal;
}


Il est toute fois possible d'éviter la déclaration et l'utilisation de la variable retVal. Pour ce faire, on peut utiliser les mots-clés yield return dans un bloc itérateur.
Le mot-clé yield indique au compilateur que la méthode dans laquelle il apparait est un bloc itérateur. Le compilateur génère une classe pour implémenter le comportement exprimé dans le bloc itérateur. Dans le bloc itérateur, le mot clé yield est utilisé avec le mot clé return pour fournir une valeur à l'objet énumérateur. C'est donc cette valeur qui est retournée.


L'exemple ci-dessous reprend effectue exactement la même opération, mais en évitant la variable retVal.



public IEnumerable<int> GetPairNumber(int From, int To)
{
for (int indexNumber = From; indexNumber < To; indexNumber++)
{
if (indexNumber % 2 == 0)
{
yield return indexNumber;
}
}
}


Ok ça ne changera pas le monde, mais c’est un petit truc intéressant à savoir :)

jeudi 15 juillet 2010

Contrôler l'arrêt d'un service Windows perso (.NET)

Lorsqu'on développe un service Windows, il est essentiel de contrôler le moment où le service s'arrêtera surtout lorsqu'il s'agit d'un traitement en lot. Vous ne voudriez probablement pas que votre service s'arrête en plein traitement, car un utilisateur à lancer la commande net stop votre_service.

Prenons par exemple un service qui roule en continu et qui vérifie toutes les 15 secondes les enregistrements qui ont été ajoutés à une base de données afin de pouvoir les traiter. Dans cet exemple, le service ne doit pas s'arrêter tant qu'il n'a pas fini de traiter les nouveaux enregistrements.

Seul le code servant au contrôle du service sera présenté dans cette publication

Voici le code du service ServiceData (ServiceData.cs)


public partial class ServiceData : ServiceBase
{
Thread dataProcessingThread = null;

public ServiceData()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
dataProcessingThread = new Thread(DataProcessing.ProcessPendingData);
dataProcessingThread.Start();
}

protected override void OnStop()
{
DataProcessing.ResetEvent.Set();
while (dataProcessingThread.IsAlive)
{
this.RequestAdditionalTime(5000);
Thread.Sleep(5000);
}
}
}


Lors du démarrage du service, nous lançons, dans l'évènement OnStart, un thread qui va s'exécuter tout au long du service. Nous verrons la classe DataProcessing un peu plus loin... Regardons tout d'abord l'arrêt du service. Dans la fonction OnStop la commande
DataProcessing.ResetEvent.Set();
sert à déclencher l'évènement d'arrêt du service. La boucle while sert ensuite à attendre que les enregistrements restant à traiter se terminent. La commande suivante
this.RequestAdditionalTime(5000);
sert à avertir le Service Control Manager (SCM) que le service requiert 5 secondes de plus pour s'arrêter. Si nous omettons cette commande, le SCM croira que le service ne répond plus. Cette boucle vérifie en fait à tout les 5 secondes si le travail du thread est terminé.


Voici le code de la classe DataProcessing (DataProcessing.cs)


public static class DataProcessing
{
private static ManualResetEvent resetEvent = new ManualResetEvent(false);
public static ManualResetEvent ResetEvent
{
get { return resetEvent; }
set { resetEvent = value; }
}

public static void ProcessPendingData()
{
do
{
//All stuff here will not be interrupted until they finish
}
while (!ResetEvent.WaitOne(15000));
}
}


Le code suivant
private static ManualResetEvent resetEvent = new ManualResetEvent(false);
public static ManualResetEvent ResetEvent
{
get { return resetEvent; }
set { resetEvent = value; }
}

définit le ResetEvent qui sert à avertir le thread qu'un événement est survenu.
public static void ProcessPendingData()
{
do
{
//All stuff here will not be interrupted until they finish
}
while (!ResetEvent.WaitOne(15000));
}
La méthode statique ProcessPendingData sert à traiter les enregistrements. Tout ce qui se trouve à l'intérieur du do-while ne sera pas interrompu par un arrêt de service.
Donc la boucle va s'exécuter toutes les 15 secondes tant et aussi longtemps qu'une demande d'arrêt n'a pas été formulée. Votre lot d'enregistrements pourra donc être traité sans être interrompu!

vendredi 12 mars 2010

Effectuer un postback complet de la page à partir d'un UpdatePanel

L'UpdatePanel permet d'actualiser certaines parties de la page sans pour autant rafraîchir toute la page. Les postbacks, effectués par les contrôles à l'intérieur de l'UpdatePanel, seront gérés par l'UpdatePanel, ce qui aura pour effet d'actualiser partiellement la page. Il peut toute fois arrivé dans des cas précis qu'il faille rafraîchir toute la page. Dans ce cas vous pouvez, dans le code-behind de votre page, ajouter le code suivant :
ScriptManager.RegisterClientScriptBlock(Page, typeof(Page),"PagePostBack","window.location.href='" + Request.Url.ToString() + "';", true);
L'instruction ci-dessus enregistre un script de redirection qui sera exécuté côté client lors de l'actualisation de l'UpdatePanel.

lundi 1 mars 2010

Sauvegarder manuellement une données saisies par un PeopleEditor

Supposons que nous avons un PeopleEditor configuré comme suit :
<%@ Register Tagprefix="SharePoint"  Namespace="Microsoft.SharePoint.WebControls"  Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,  PublicKeyToken=71e9bce111e9429c" %>
<SharePoint:PeopleEditor id="peopleEditorEmployee" runat="server" autopostback="true" allowempty="false" placebuttonsunderentityeditor="false" rows="1" selectionset="User" multiselect="false">

Le contrôle ci-dessus permet de sélectionner seulement un utilisateur SharePoint et la sélection d'un utilisateur est obligatoire.

Code-behind :
(PickerEntity)peoplePickerEmploye.ResolvedEntities[0];
String result = employee.EntityData["SPUserID"].ToString() + ";#" + employee.EntityData["DisplayName"].ToString();
SPListItem itemToModified = SPContext.Current.Web.Lists["<Votre liste>"].GetItemById(int_id);
itemToModified["Champ a mettre à jour"] = result;
itemToModified.Update();