Skip to main content

Le blog de Stéphane Eyskens

Go Search
Le blog de Stéphane Eyskens
  

 

 

FR/EN

Retrouvez mon espace sur Developpez.com, l'espace Sharepoint sur developpez.com. Pour toute question relative à Sharepoint, vous pouvez tenter votre chance dans ce forum 

Reduce All
 Wygday
Salut,
 
Juste pour vous dire que je vais bientôt animer une session WygDay avec Julien Chable (http://blogs.developpeur.org/neodante/) sur les workflows et Forms Services. Pour plus d'infos, vous pouvez consulter le site officiel
 
 
A bientôt :)
Posted by Stéphane Eyskens at 5/11/2008 9:13:49 PM | Comments(0)
 Limite du nombre de valeurs de lookup
Salut,
 
Vous l'avez peut-être déjà constaté sinon ce sera une mauvaise nouvelle mais il y a une limite à ne pas dépasser lorsque vous utilisez une colonne standard de type "lookup". Si la liste ciblée par la colonne contient plusieurs milliers de lignes, Internet Explorer 7 (peut-être que le 6 aussi...) a du mal à afficher la dite colonne dans les formulaires d'insertion et d'édition.
 
L'alternative consiste soit :
 
  • à limiter le nombre d'éléments dans la liste ciblée
  • à développer un custom field qui utilise l'AJAX pour permettre des recherches de valeurs auto-complétées  et d'ainsi limiter le nombre d'éléments affichés dans la lookup list.
  • Eventuellement développer un custom field qui permette de faire un filtre sur la liste lookup source afin de n'afficher que certaines valeurs dans la colonne lookup mais ce sera moins performant et moins confortable pour l'utilisateur que la solution précédente.
Cette limite n'est en aucun cas une limite de Sharepoint qui pour sa part, restitue les milliers de lignes en un clin d'oeil, c'est réellement une limite d'IE qui peine à dérouler la liste déroulante :)
 
En tous cas, il faut tenir compte de cette limite lors de vos implémentations sinon gare aux mauvaises surprises :).
 
Posted by Stéphane Eyskens at 4/28/2008 9:25:24 PM | Comments(0)
 Eléments de menu du SPGridView
Salut,
 
Je travaille depuis quelque temps avec le SPGridView et jusqu'à présent, je n'ai jamais trouvé de moyen d'afficher les options du menu de manière dynamique selon la valeur d'une ou plusieurs cellules de ligne. J'ai commencé à regarder le code client et j'ai trouvé un début de réponse.
 
L'image ci-dessous illustre ce que je veux faire, disons qu'on a une table avec deux lignes et deux colonnes
 
 
et je veux afficher "Option 1" seulement lorsque la valeur de la colonne 2 est égale à "Row 1 - Cell 2"....
 
Dans le cas présent, "Option 1" et "Option 2" doivent être affichées pour la première ligne et seulement "Option 2" pour la deuxième ligne.
 
Dans l'API, il y a deux moyens de restreindre la visibilité des options d'un menu de SPGridView.
 
- La première méthode consiste à attribuer un niveau de permission comme par exemple:
 
MenuItem.Permissions = SPBasePermissions.AddListItems;
 
mais ceci restreint la visibilité pour toutes les options toutes lignes confondues et en fonction d'une permission et non d'une valeur contextuelle d'une colonne de ligne.
 
- La deuxième méthode consiste à utiliser HiddenScript qui prend en paramètre "True" ou "False" pour cacher ou non une option du menu.
 
Si vous spécifiez "true" ou "false" dans votre code, l'option de menu sera visible ou non pour la liste entière, or ce n'est pas ce que nous souhaitons.
 
Nous voulons que "Option 1" soit visible seulement si la colonne 2 d'une ligne contient la valeur "Row 1 - Cell 2".
 
Pour ce faire, nous devons créer une fonction javascript qui retourne true ou false selon une valeur de votre choix. Cette fonction contextuelle s'appliquera seulement sur la ligne ciblée par le clic de souris.
 
Voici un début de réponse que j'ai pu trouver mais ce n'est certainement pas la panacée universelle car certainement peu portable et quelque peu "tordue" mais bon, je vous la livre quand même :)....
 
En examinant le code client généré par le SPGridView et son menu, j'ai pu identifier la manière de cibler l'élément qui fallait pour récupérer la valeur de(s) celllule(s) que je voulais comparer pour afficher ou non une option. Il existe peut-être déjà quelque part une fonction javascript Sharepoint qui fait ce que les miennes font et probablement mieux mais je ne les ai pas encore trouvées et je n'ai pas encore eu beaucoup le temps d'investiguer plus.
 
Voici donc ce que j'ai trouvé mais je le rappelle, ce n'est certainement pas meilleure méthode mais cela constitue au moins une piste
 
Créer les fonctions javascript
 
<script language='javascript'>
function GetColumnValue(Target,ColumnIndex)

  if(Target)
  {
    if(Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.tagName == 'TR')
    {
        return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;   
    }
    else
    {
        return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;
    }
  }
}
function DisplayMenuItem(Target,ColumnIndex,ValueToCompare)

   return (GetColumnValue(Target,ColumnIndex) != ValueToCompare)
  }
</script>
 
La fonction GetColumnValue récupère le innerText de la cellule ciblée par la variable "ColumnIndex" de la ligne courante.
 
La fonction DisplayMenuItem retourne "true" ou "false" selon le résultat de la comparaison. Notez que HiddenScript cache l'option si la valeur retournée est "true" et l'affiche dans le cas contraire.
 
Appeler les fonctions javascript
 
Dans votre code, vous pouvez appeler ces fonctions comme ceci:
 
YourMenuItem.HiddenScript = "DisplayMenuItem(this,1,'Row 1 - Cell 2')";
 
Ceci compare la valeur de la 2ème cellule (index 1) à la valeur 'Row 1 - Cell 2' et affiche "Option 1" seulement si celles-ci sont égales.
 
Vous pouvez combiner plusieurs critères comme ceci:
 
YourMenuItem.HiddenScript = "if(DisplayMenuItem(this,0,'Row 1 - Cell 1') || DisplayMenuItem(this,0,'Row 1 - Cell 2')) true; else false;";
 
Dans ce cas, "Option 1" apparaîtra seulement si la valeur de la première colonne est "Row 1 - Cell 1" et celle de la 2ème est "Row 1 - Cell 2".
 
Le Code en entier
 
 
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebControls;
using System.Data;

namespace SPGridViewWp
{
    public class Grid : System.Web.UI.WebControls.WebParts.WebPart
    {
       
        SPGridView SPGridViewExample = null;
        const string SPGridViewExampleMenuID = "TheMenu";
        /// <summary>
        /// Client code to handle the SPGridView menu items. As stated in my blog this is certainly not the beset
        /// way of doing that but it could be considered as a good start.
        /// </summary>
        /// <returns></returns>
        private string ClientScript()
        {
            StringBuilder ClientCode = new StringBuilder();
            ClientCode.Append("<script language='javascript'>");
            ClientCode.Append("function GetColumnValue(Target,ColumnIndex)");
            ClientCode.Append("{");
            ClientCode.Append("  if(Target){");
            ClientCode.Append("    if(Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.tagName == 'TR')");
            ClientCode.Append("    {");
            ClientCode.Append("         return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;");
            ClientCode.Append("    }else");
            ClientCode.Append("    {");
            ClientCode.Append("         return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;");
            ClientCode.Append("    }");
            ClientCode.Append("  }");
            ClientCode.Append("}");
            ClientCode.Append("function DisplayMenuItem(Target,ColumnIndex,ValueToCompare)");
            ClientCode.Append("{");
            ClientCode.Append("   return (GetColumnValue(Target,ColumnIndex) != ValueToCompare);");
            ClientCode.Append("}");
            ClientCode.Append("</script>");
            return ClientCode.ToString();
        }
        protected override void CreateChildControls()
        {
            try
               
            {
                //in a real context, isolate the function in a .js file
                Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "CustomMenuScript", ClientScript());
                this.Controls.Clear();
                //Building the datatable
                DataTable SPGridViewData = new DataTable();
                SPGridViewData.Columns.Add(new DataColumn("Column 1"));
                SPGridViewData.Columns.Add(new DataColumn("Column 2"));
                DataRow NewRow = SPGridViewData.NewRow();
                NewRow["Column 1"] = "Row 1 - Column 1";
                NewRow["Column 2"] = "Row 1 - Column 2";
                SPGridViewData.Rows.Add(NewRow);
                NewRow = SPGridViewData.NewRow();
                NewRow["Column 1"] = "Row 2 - Column 1";
                NewRow["Column 2"] = "Row 2 - Column 2";
                SPGridViewData.Rows.Add(NewRow);              
                SPGridViewExample = new SPGridView();               
                SPGridViewExample.AutoGenerateColumns = false;
                #region Menu Template
                MenuTemplate SPGridViewExampleMenu = new MenuTemplate();
                SPGridViewExampleMenu.ID = SPGridViewExampleMenuID;            
                MenuItemTemplate SPGridViewExampleMenuItem = new MenuItemTemplate("Option 1");
                //HiddenScript call
                SPGridViewExampleMenuItem.HiddenScript = "DisplayMenuItem(this,1,'Row 1 - Column 2')";                                                     
                SPGridViewExampleMenu.Controls.Add(SPGridViewExampleMenuItem);
                //No hidden script -> always visible
                SPGridViewExampleMenuItem = new MenuItemTemplate("Option 2");               
                SPGridViewExampleMenu.Controls.Add(SPGridViewExampleMenuItem);
                this.Controls.Add(SPGridViewExampleMenu);
                SPMenuField Column1 = new SPMenuField();
                Column1.HeaderText = "Column 1";
                Column1.TextFields = "Column 1";
                Column1.MenuTemplateId = SPGridViewExampleMenuID;
                SPGridViewExample.Columns.Add(Column1);             
                #endregion
                #region columns
                SPBoundField Column2 = new SPBoundField();               
                Column2.HeaderText = "Column 2";
                Column2.DataField = Column2.SortExpression = "Column 2";
                SPGridViewExample.Columns.Add(Column2);               
                #endregion
                SPGridViewExample.DataSource = SPGridViewData;
                this.Controls.Add(SPGridViewExample);
                SPGridViewExample.PagerTemplate = null;
                SPGridViewExample.DataBind();
            }
            catch (Exception ex) { Context.Response.Write(ex.Message); }
        }
          
    }
}
 
 
Remarque
 
Cette technique ne fonctionne pour l'instant qu'avec Internet Explorer. Je n'ai pas encore généré le code client nécessaire pour gérer ça avec d'autres navigateurs (firefox notamment)
 
C'est donc une méthode à utiliser avec précaution ou selon un contexte bien précis. N'hésitez pas à poster des commentaires si vous connaissez d'autres moyens.
 
 
 
Posted by Stéphane Eyskens at 4/26/2008 4:49:48 PM | Comments(1)
 SPGridView menu items dynamic rendering
Hello,
 
I've been working with the SPGridView for a while and I've never been able to find out how to display a menu item according to the value of one or more cell(s) of the targetted row until I dived into the client code generated by Sharepoint
 
So, here is an example to understand what I wanted to achieve. Say, we have a data table with 2 rows and 2 columns
 
 
 
and I want to display Option 1 only when the value of column 2 is equal to "Row 1 - Cell 2"....
 
In our case, both options should be displayed for the first row and only Option 2 should be displayed for the 2nd row.
 
In the API, there are two ways to restrict the visibility of a specific menu item
 
- The first way is to specify a permission level to the menu item's permission property like the following for instance:
 
MenuItem.Permissions = SPBasePermissions.AddListItems;
 
But this restricts the visibility according to a permission level not to the value of a cell....
 
- The other way is to use the property HiddenScript that expects a boolean value of "True" or "False" to hide/not hide the menu item.
 
If you specify the value "true" or "false" in your code, the targetted menu item will be visible or not for the entire list and that's not what we're looking for.
 
We'd like "Option 1" to be visible only if the 2nd cell of the row contains the value "Row 1 - Cell 2".
 
Therefore, we've got to build a javascript function that will return true or false according to the value of your choice. This contextual function will be applied on the row for which you want to extend the menu.
 
Here is what I found so far to make it working but I need to investigate further since I find this process a bit tedious....I've been diving into the client code to see how the menu items were built-up and I could target the right object to retreive the value of the cell(s) I wanted to compare. There might be existing Sharepoint javascript functions doing what my own do and probably in a proper way but I hadn't time yet to dive a little more. So, I wanted to share my findings on this topic even if it's certainly not yet the best way to proceed.
 
Creating the javascript functions
 
<script language='javascript'>
function GetColumnValue(Target,ColumnIndex)

  if(Target)
  {
    if(Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.tagName == 'TR')
    {
        return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;   
    }
    else
    {
        return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;
    }
  }
}
function DisplayMenuItem(Target,ColumnIndex,ValueToCompare)

   return (GetColumnValue(Target,ColumnIndex) != ValueToCompare)
  }
</script>
 
The function GetColumnValue gets the innerText of the column targetted by the "ColumnIndex" variable for the current row.
 
The function DisplayMenuItem returns "true" or "false" according to the result of the value comparison. Note that HiddenScript hides the menu item if the returned value is "true" and displays it otherwise.
 
Calling the javascript functions
 
From your code, you can call the javascript functions this way:
 
YourMenuItem.HiddenScript = "DisplayMenuItem(this,1,'Row 1 - Cell 2')";
 
This will compare the 2nd cell (index 1) with the value 'Row 1 - Cell 2') and will display the option "Option 1" only if the value of cell 2 equals "Row 1 - Cell 2".
 
You can combine the function calls if you want to check multiple critera, this way:
 
YourMenuItem.HiddenScript = "if(DisplayMenuItem(this,0,'Row 1 - Cell 1') || DisplayMenuItem(this,0,'Row 1 - Cell 2')) true; else false;";
 
In that case, the menu item "Option 1" will only show up when the value of the first column is "Row 1 - Cell 1" and when the value of the 2nd one is "Row 1 - Cell 2".
 
Full Code
 
Here is a full code sample
 
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebControls;
using System.Data;

namespace SPGridViewWp
{
    public class Grid : System.Web.UI.WebControls.WebParts.WebPart
    {
       
        SPGridView SPGridViewExample = null;
        const string SPGridViewExampleMenuID = "TheMenu";
        /// <summary>
        /// Client code to handle the SPGridView menu items. As stated in my blog this is certainly not the beset
        /// way of doing that but it could be considered as a good start.
        /// </summary>
        /// <returns></returns>
        private string ClientScript()
        {
            StringBuilder ClientCode = new StringBuilder();
            ClientCode.Append("<script language='javascript'>");
            ClientCode.Append("function GetColumnValue(Target,ColumnIndex)");
            ClientCode.Append("{");
            ClientCode.Append("  if(Target){");
            ClientCode.Append("    if(Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.tagName == 'TR')");
            ClientCode.Append("    {");
            ClientCode.Append("         return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;");
            ClientCode.Append("    }else");
            ClientCode.Append("    {");
            ClientCode.Append("         return Target.event.srcElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.cells[ColumnIndex].innerText;");
            ClientCode.Append("    }");
            ClientCode.Append("  }");
            ClientCode.Append("}");
            ClientCode.Append("function DisplayMenuItem(Target,ColumnIndex,ValueToCompare)");
            ClientCode.Append("{");
            ClientCode.Append("   return (GetColumnValue(Target,ColumnIndex) != ValueToCompare);");
            ClientCode.Append("}");
            ClientCode.Append("</script>");
            return ClientCode.ToString();
        }
        protected override void CreateChildControls()
        {
            try
               
            {
                //in a real context, isolate the function in a .js file
                Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "CustomMenuScript", ClientScript());
                this.Controls.Clear();
                //Building the datatable
                DataTable SPGridViewData = new DataTable();
                SPGridViewData.Columns.Add(new DataColumn("Column 1"));
                SPGridViewData.Columns.Add(new DataColumn("Column 2"));
                DataRow NewRow = SPGridViewData.NewRow();
                NewRow["Column 1"] = "Row 1 - Column 1";
                NewRow["Column 2"] = "Row 1 - Column 2";
                SPGridViewData.Rows.Add(NewRow);
                NewRow = SPGridViewData.NewRow();
                NewRow["Column 1"] = "Row 2 - Column 1";
                NewRow["Column 2"] = "Row 2 - Column 2";
                SPGridViewData.Rows.Add(NewRow);              
                SPGridViewExample = new SPGridView();               
                SPGridViewExample.AutoGenerateColumns = false;
                #region Menu Template
                MenuTemplate SPGridViewExampleMenu = new MenuTemplate();
                SPGridViewExampleMenu.ID = SPGridViewExampleMenuID;            
                MenuItemTemplate SPGridViewExampleMenuItem = new MenuItemTemplate("Option 1");
                //HiddenScript call
                SPGridViewExampleMenuItem.HiddenScript = "DisplayMenuItem(this,1,'Row 1 - Column 2')";                                                     
                SPGridViewExampleMenu.Controls.Add(SPGridViewExampleMenuItem);
                //No hidden script -> always visible
                SPGridViewExampleMenuItem = new MenuItemTemplate("Option 2");               
                SPGridViewExampleMenu.Controls.Add(SPGridViewExampleMenuItem);
                this.Controls.Add(SPGridViewExampleMenu);
                SPMenuField Column1 = new SPMenuField();
                Column1.HeaderText = "Column 1";
                Column1.TextFields = "Column 1";
                Column1.MenuTemplateId = SPGridViewExampleMenuID;
                SPGridViewExample.Columns.Add(Column1);             
                #endregion
                #region columns
                SPBoundField Column2 = new SPBoundField();               
                Column2.HeaderText = "Column 2";
                Column2.DataField = Column2.SortExpression = "Column 2";
                SPGridViewExample.Columns.Add(Column2);               
                #endregion
                SPGridViewExample.DataSource = SPGridViewData;
                this.Controls.Add(SPGridViewExample);
                SPGridViewExample.PagerTemplate = null;
                SPGridViewExample.DataBind();
            }
            catch (Exception ex) { Context.Response.Write(ex.Message); }
        }
          
    }
}
 
Additional Remark
 
Note that this technique does only work with Internet Explorer. I've not found yet the means to make it work with another browser (firefox).
 
Use this technique with caution and post your comments if you have any other idea to handle that.
 
 
Posted by Stéphane Eyskens at 4/26/2008 4:25:50 PM | Comments(0)
 Comment gérer une course critique avec une requête CAML?
En CAML, contrairement au SQL on ne peut pas exécuter une requête et en même temps verrouiller en écriture la liste sur laquelle cette requête s'exécute.
 
Si vous devez pour une raison particulière, avoir la certitude que ce que vous récupérez dans une SPListItemCollection correspond bien à l'état actuel des données, vous êtes bloqué car il n'y a aucune méthode en standard permettant de verrouiller la liste/bibliothèque concernée.
 
Il existe bien des possibilités empechant tout utilisateur de modifier les données d'un site.
 
Par exemple, si vous faites ceci:
 
using(SPSite UnSite = new SPSite("url"))
{
  UnSite.WriteLocked = true;
}
 
Aucun utilisateur ne pourra insérer/modifier/supprimer des données de ce site. Il n'est bien sûr pas envisageable de bloquer un site entier lorsque l'on souhaite simplement verrouiller une liste temporairement.....
 
 
Y a-t-il une autre solution?
 
Pas vraiment, il existe bien ceci :
 
using(SPSite UnSite = new SPSite("url"))
{
  using(SPWeb UnWeb = UnSite.OpenWeb())
  {
      SPList UneListe = null;     
      int CurrentWriteLevel=-1;
      try
      {
         UneListe = UnWeb.Lists["UneListe"];
         CurrentWriteLevel = UneListe.WriteSecurity;
         UneListe.WriteSecurity = 4;
         UneListe.Update();
 
      }
      catch(...)
      {
         ...
      }
      finally
      {
          if(UneListe != null)
          {
             UneListe.WriteSecurity = CurrentWriteLevel;
             UneListe.Update();
          } 
      }
  }
}
 
les niveaux de sécurité vont de 1 à 4 et sont du plus permissif au plus restrictif.
 
4 signifie que les contributeurs ne peuvent plus modifier/supprimer/insérer aucun élément dans la liste ciblée. Par contre, un utilisateur ayant tous les droits (full control) pourra...
 
Donc, d'une part, cette méthode ne remplit pas tout à fait le cahier des charges puisqu'elle ne verrouille pas la liste pour tout le monde et d'autre part, un utilisateur tentant d'alimenter cette liste se verra retourner une exception de type "Accès Refusé" qui pourrait faire croire à un problème de paramétrage de sécurité.
 
Moralité : il n'y a pas moyen de s'assurer à 100% que les résultats retournés par une SPQuery correspondent bien à l'état courant des données sauf si vous verrouillez le site, ce qui n'est quoi qu'il en soit pas réellement envisageable.
 
Les deux pistes ci-dessus peuvent néanmoins dans certains cas extrêmes demeurer un secours potentiel.
Posted by Stéphane Eyskens at 4/19/2008 6:05:57 PM | Comments(0)
 Quelques questions/réponses intéressantes croisées sur quelques forums
Voici quelques questions que j'ai croisé sur des forums divers qui me semblent intéressantes. Désormais, chaque semaine, j'essayerai de poster un résumé des meilleurs questions rencontrées et qui ont eu une réponse.
 
Comment faire l'équivalent d'un SELECT TOP 5 ....FROM ...en CAML?
 
Vous spécifiez simplement une valeur pour la propriété RowLimit de l'objet SPQuery.
 
SPQuery UneQuery = new SPQuery();
UneQuery.Query = "<Where></Where>"....
UneQuery.RowLimit = 5;
 
 
 
Comment inclure tous les documents d'une bibliothèque, y compris ceux situés dans des répertoires?
 
Vous spécifiez le mode récursif à la propriété ViewAttributes de l'objet SPQuery.
 
SPQuery UneQuery = new SPQuery();
UneQuery.Query = "<Where></Where>"....
UneQuery.ViewAttributes = "Scope='Recursive'";
 
 
Comment augmenter la taille autorisée pour les fichiers en upload?
 
 
Comment exécuter stsadm depuis un composant et sous quelle identité?
 
Réponse complète :
Posted by Stéphane Eyskens at 4/12/2008 3:48:18 PM | Comments(0)
 Liens par e-mail ne fonctionnent pas avec des caractères accentués
Récemment, un de mes collègues (Gregory Royer) a été confronté au problème suivant:
 
Vous disposez d'une bibliothèque de documents et lorsque vous souhaitez envoyer le lien par mail, le lien présenté dans le corps du mail est corrompu. Ceci se produit lorsque vous avez des caractères accentués ou spéciaux.
 
Il s'agit d'un bug. Voici la solution proposée:
 
  • Editez core.js
  • Faites une recherche sur la ligne

    fileUrl=escapeProperly(httpRootWithSlash.substr(0, slashLoc))+currentItemUrl;

    et remplacez-là par

    fileUrl=httpRootWithSlash.substr(0, slashLoc)+currentItemUrl;

    Quelques lignes plus bas, vous trouverez la ligne

    strAction="javascript:navigateMailToLink.....

    Placez-y juste au dessus cette ligne

    fileUrl = encodeURI( decodeURI( fileUrl ) );

Et voilà (merci Greg), enregistrez vos changements et cela fonctionnera. D'habitude les modifications dans core.js sont plutôt à proscrire mais bon, il s'agit d'un cas de force majeure :).

Posted by Stéphane Eyskens at 4/12/2008 12:36:51 PM | Comments(0)
 Ajouter des lignes dynamiquement à une repeating table dans Forms Services
Dans Infopath Smart Client, il est aisé d'ajouter des lignes à une repeating table via la méthode ExecuteAction(). Celle-ci n'est pas disponible sous Forms Services car le modèle objet est moins riche que celui du Smart Client.
 
En Forms Services, tout est XML, il est inutile d'essayer d'intervenir sur un contrôle directement, il faut toujours viser sa source de données.
 
Voici comment ajouter des lignes à une repeating table dans Forms Services.
 
Dans cet exemple, je vais faire comme si j'étais dans le contexte d'un workflow, dans un formulaire d'initiation où je propose à l'utilisateur d'inclure les fichiers attachés d'un élément de liste que je copie et je propose tous les fichiers un à uns avec une case à cocher. L'image ci-dessous montre le résultat souhaité
 
Repeating Table
L'utilisateur dans ce cas n'aurait plus qu'à cliquer sur Ok pour inclure tous les fichiers attachés ou il pourrait décider de n'inclure que ce qu'il souhaite.
 
Création d'un formulaire et de la source de données
 
La source de données ressemble à ceci
 
datasource
Le groupe "Fichiers" étant en mode "Repeating", il suffit de faire un clic droit dessus et de choisir "Repeating Table" pour obtenir ceci
 
repeating
 
C'est à dire une table répétitive avec les deux contrôles (Textbox à gauche, case à cocher à droite) non remplis.
 
Ajout du code dans l'évènement "Loading" du formulaire
 
public void FormEvents_Loading(object sender, LoadingEventArgs e)
        {
            XmlWriter AddLines = null; //Sert à ajouter des lignes
            try
            {
                //On se crée un navigateur pour manipuler le source XML
                XPathNavigator RootNode = MainDataSource.CreateNavigator();
                //On initialise la première ligne de notre table.
                RootNode.SelectSingleNode("/my:DonneesInitiation/my:Fichiers/my:Fichier",
                    NamespaceManager).SetValue("fichier 1");
                RootNode.SelectSingleNode("/my:DonneesInitiation/my:Fichiers/my:Inclure",
                    NamespaceManager).SetValue("true");
                //
                AddLines =
                    RootNode.SelectSingleNode(
                    "/my:DonneesInitiation", NamespaceManager).AppendChild();
              
                string NameSpace = NamespaceManager.LookupNamespace("my");
                for (int i = 2; i <= 5; i++)
                {
                    //Ajout d'une occurrence de groupe
                    AddLines.WriteStartElement("Fichiers", NameSpace);                   
                    //Définition de la valeur du fichier. Dans le contexte d'un workflow
                    //ce serait simplement le nom du fichier attaché, faites donc
                    //preuve d'imagination :)
                    AddLines.WriteElementString("Fichier", NameSpace, string.Format("fichier {0}", i.ToString()));
                    //On précoche la case.
                    AddLines.WriteElementString("Inclure", NameSpace, "true");
                    AddLines.WriteEndElement();
                }              
            }
            catch (Exception Ex)
            {
                //traiter erreur
            }
            finally
            {
                //fermeture de notre writer
                if(AddLines!=null)
                    AddLines.Close();
            }
        }
 
Voilà, vous obtenez le résultat illustré par la première image.
 
 
Posted by Stéphane Eyskens at 4/5/2008 7:34:06 PM | Comments(0)
 Manipuler dynamiquement le contenu des listbox dans Forms Services
En Infopath Forms Services, tout est XML et tout est datasource. Quand on a compris cela, on a tout compris.
 
Pour manipuler le contenu de combobox, il suffit de lier sa combo à un data source XML "de base" et ensuite alimenter le datasource.
 
Exemple:
 
  • Créez un nouveau formulaire Infopath

    Ajoutez-y un champ dans votre datasource principal



    Vous obtenez ceci :

  • Créez ensuite un fichier XML avec le contenu suivant

    <?xml version="1.0" encoding="UTF-8" ?>
    <Entrees>
    <Entree><Valeur/><Texte/></Entree>
    <Entree><Valeur/><Texte/></Entree>
    </Entrees>

    Vous devez mettre deux lignes car il faut impérativement avoir un repeating group pour pouvoir lier une listbox Infopath à une source de données
  • Créez le datasource dans votre formulaire et liez-le au fichier XML. Pour ce faire, dans Infopath

    Data Connections => Add => Receive Data => Xml Document => Votre fichier => Appelez-là "DonneesListe"
  • Ceci fait, liez le champ précédemment créé à cette source de données, double-cliquez dessus et configurez comme illustré ci-dessous



  • Ensuite, ajoutez deux boutons à votre formulaire.


    Et liez-y du code (cliquez dessus, edit form code)
  • Voici le code complet de mon petit formulaire qui montre comment ajouter/supprimer les éléments de la liste


private void ViderTout()
{
  XPathNavigator Donnees =
     DataSources["DonneesListe"].CreateNavigator().SelectSingleNode("/Entrees");

  if (Donnees != null)
  {
     XPathNavigator PremiereEntree = Donnees.SelectSingleNode("/Entrees/Entree[1]", NamespaceManager);
     XPathNavigator DerniereEntree = Donnees.SelectSingleNode("/Entrees/Entree[last()]", NamespaceManager);
     if (PremiereEntree != null && DerniereEntree != null)
     {
 Donnees.MoveTo(PremiereEntree);
 Donnees.DeleteRange(DerniereEntree);
     }
  }
}

//Ajout d'une entrée

private void AjouterEntree(string Valeur, string Texte)
{
  string NouvelleEntree =
  string.Format("<Entree><Valeur>{0}</Valeur><Texte>{1}</Texte></Entree>",
         Valeur, Texte);
  XPathNavigator DonnesActuelles =
 DataSources["DonneesListe"].CreateNavigator().SelectSingleNode("//Entrees",NamespaceManager);

  if(DonnesActuelles != null)
    DonnesActuelles.AppendChild(NouvelleEntree);
}

//Appelé lors du clic sur Ajouter

public void Ajouter_Clicked(object sender, ClickedEventArgs e)
{
  for (int i = 0; i < 5; i++)
  {
    AjouterEntree(i.ToString(), "item " + i);
  }
}

//Appelé lors du clic sur Vider

public void Vider_Clicked(object sender, ClickedEventArgs e)
{
  ViderTout();
}

 

 

  • Y a plus qu'à compiler et à tester

A priori tout devrait fonctionner :)

Posted by Stéphane Eyskens at 3/15/2008 5:48:25 PM | Comments(0)

>>

 ‭(Hidden)‬ Content Editor Web Part ‭[1]‬