Accueil Nos publications Blog Conseils pour développer des rapports RDLC dans Visual Studio

Conseils pour développer des rapports RDLC dans Visual Studio

Visual Studio 2010 La création d’un rapport de données est une fonctionnalité classique des logiciels en particulier ceux à destination de professionnels. Or, les technologies pour le faire ne sont pas toujours connues. Examinons le cas de Visual Studio.

Dans sa philosophie de proposer un AGL complet, Microsoft incorpore dans Visual Studio ce qu’il faut pour réaliser des rapports de données. Cela se faisait initialement au moyen de Crystal Reports. Depuis la version 2005 et le framework .NET, Visual Studio intègre sa propre technologie de conception et génération de rapports, très proche de celle de SSRS.

Le concepteur de rapports de Visual Studio est de premier abord intuitif, aisé à prendre à main. Il est en fait assez difficile à maîtriser. Voici quelques généralités sur ces rapports, suivies de conseils pour une bonne conception et éviter les mauvais surprises au rendu final (en PDF par ex.)

Généralités

Type de fichier

Selon le type de projet il faut référencer la librairie suivante :

  • Windows : Microsoft.ReportViewer.WinForms
  • Web : Microsoft.ReportViewer.WebForms

Un rapport est alors un fichier de type “Report”, d’extension “.rdlc” :

RDLC - Add New Item

Par défaut, un fichier RDLC s’ouvre avec le Report Designer. Il s’agit en fait d’un fichier XML :

RDLC - Open with

<?xml version="1.0" encoding="utf-8"?>
<Report xmlns:rd="https://schemas.microsoft.com/SQLServer/reporting/reportdesigner"
        xmlns="https://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition">
  <Description>XXX</Description>
  <!-- ... -->
</Report>

Eléments constitutifs

Un rapport se compose des éléments suivants :

  • Comme un document Word : corps, entête et pied de page :
    RDLC - Document outline
  • Chacun peut accueillir des contrôles :
    RDLC - Toolbox
  • Des données : paramètres, datasets, images embarquées :
    RDLC - Report data

Page

La page en sortie est définie dans Report Properties > Page Setup :
RDLC - Report properties - Page Setup

On peut ainsi définir l’unité de mesure dans la page :

  • Les centimètres : cm
  • Les pouces : in

Note :

  • Le Designer n’est pas 100% WYSIWYG : contrairement à ce que l’on a dans Word en particulier en mode Page, les sauts et séparateurs de pages n’y sont pas figurés.
  • Header/Footer : on peut ne pas les afficher en 1ere et/ou dernier page mais la place est réservée !
  • Le moteur de rendu peut chercher à compresser en hauteur le rapport : si une TextBox est cachée (propriété Hidden=True), il est possible que tous les éléments en dessous soit remontés, ce qui peut casser la mise en page souhaitée. Il faudra alors utiliser des contrôles pour caler les autres à leur place, par exemple un Rectangle vide mais visible, de même hauteur que le texte caché, ou bien un Rectangle avec un saut de page :
    RDLC - Rectangle properties

Définition des images

Autant le rendu des textes est bon, celui des images n’est pas très fin. En effet, la définition est de 96 DPI, soit 795 pixels pour 21 cm, la largeur d’une feuille A4.

D’autre part, les images peuvent être embarquées dans le rapport. Alors, elles sont compressées assez fortement. Le rendu final une fois imprimé peut être assez décevant. Il est donc préférable de « sortir » le texte des images pour le mettre dans des TextBox puis de faire des essais pour trouver la taille d’image qui passe le mieux.

Déplacement et redimensionnement

Les éléments peuvent être déplacés et redimensionnés de manière classique :

  • A la souris :
    RDLC - Move Redim

    • Avec calage sur les autres éléments figurés par des lignes bleues :
      RDLC - Calage
    • Ou sans en maintenant la touche Ctrl appuyée, comme pour empêcher une fenêtre de se « docker » dans Visual Studio.
  • Au clavier :
    • Ctrl+Flèche pour déplacer
    • Ctrl+Maj+Flèche pour redimensionner
  • En modifiant les properties Location et Size :
    RDLC - Properties - Location Size

RDLC - Toolbar Report Formatting Pour aligner (en position ou en taille) plusieurs éléments entre eux, il peut être pratique d’utiliser la barre d’outils Report Formatting.

L’élément de référence (par rapport auquel caler les autres) est alors le premier sélectionné. C’est celui dont les points d’attache sont sur fond blanc :
RDLC - Multi Select

Enfin, pour centrer un/des élément(s) horizontalement ou verticalement dans son élément parent, utiliser le menu
Format > Center in Form :
RDLC - Menu Format - Center in Form - Result

Note : Faire attention aux éléments suivants :

  • L’agrandissement ou le déplacement d’un contrôle en dépassant la bordure de son parent (rectangle ou page body) agrandit ce dernier automatiquement. On peut vite se retrouver avec une page de 22 cm de large s’imprimant au final sur 2 pages A4 !
    RDLC - Auto extend parent
  • Le déplacement d’un rectangle container par modification de sa propriété Location ne déplace pas les contrôles enfants !
    RDLC - Parent move without its children

Visibilité conditionnelle

Il est possible de masquer un contrôle dynamiquement grâce à l’usage d’une « expression de visibility » mapée à la propriété Hidden du contrôle :
RDLC - Visibility Expression

Les expressions n’étant pas toujours bien évaluées, il est plus sûr d’avoir des expressions de ce type : =IIf(Parameters!ReportParameter1.Value = "", True, False)

Z-Index

Contrairement à la fenêtre d’outil Document Outline du designer de WinForms, son homologue pour le Designer de RDLC ne permet pas de monter ou de descendre le Z-Index d’un contrôle en modifiant l’ordre d’ajout des contrôles dans le container parent. On peut certes se servir des menus Bring Forward et Send Backward mais ils ne sont pas accessibles aisément :

  • uniquement via un clic-droit sur le contrôle, parmi le menu Layout
  • il n’y a pas d’entrée dans le menu Format
  • l’ajout des boutons correspondants dans une barre d’outil ne marche pas, les boutons restant grisés.

Par contre, les boutons Bring to Front et Send to Back sont accessibles dans la barre d’outils Report Formatting. On peut donc les utiliser en structurant le rapport grâce à des rectangles pour regrouper des contrôles. Avec un peu de pratique, on arrive facilement, sans trop de clics, à obtenir le bon empilage de contrôles.

Astuces

Report Properties

Pour afficher la boîte de dialogue Report properties, on peut :

  • Utiliser le bouton dans la barre de commandes Layout :
    RDLC - Report Properties Toolbar Button
  • Faire un clic-droit dans une zone libre du rapport et sélectionner le menu correspondant :
    RDLC - Report Properties Menu

Code custom

Dans les expressions, on peut appeler des fonctions personnalisées, ce qui peut être plus propres et lisibles, et permet la réutilisation de portion de code. Ces fonctions sont définies dans Report properties > Code et s’appelent dans une expression en les préfixant avec Code..

A noter qu’il n’y a pas de coloration syntaxique à cet endroit ! On peut s’en sortir en utilisant Excel pour concevoir et tester nos fonctions, modulo le fait que leur “VBA” diffère un peu :

  • Type par défaut :
    • Excel : Variant
    • RDLC : Object
  • Spécifique à Excel :
    • Debug.Print ➜ A commenter.
    • * Is Nothing et IsEmpty(*) ➜ Utiliser IsNothing(*) dans les RDLC.
    • Pour remplacer IsNothing(*) des RDLC, on peut ajouter cette fonction dans Excel :
Function IsNothing(vValue) As Boolean
    If IsObject(vValue) Then
        IsNothing = vValue Is Nothing
    Else
        IsNothing = IsEmpty(vValue)
    End If
End Function

Touche [Échap]

La touche Échap permet de :
– Sortir une TextBox du mode édition, mode par défaut lorsque l’on clique sur le texte qu’elle contient ou en son centre.
– Faire « remonter » la sélection dans la hiérarchie enfant → parent des contrôles.

TextBox

Une TextBox permet d’afficher du texte en mode riche i.e. avec une mise en forme, sur plusieurs lignes, mais uniquement écrit horizontalement. Elle supporte l’alignement vertical et une marge intérieure (padding) :
RDLC - Text Box Properties - Alignment

Sa valeur peut combiner du texte statique avec des expressions, avec deux cas de figure simples :

  • Texte statique
  • Expression unique, modifiable directement par clic-droit :
    RDLC - Text Box - Single Expression
  • Ces deux cas se concurrencent :
    • Du fait du mode « Expression unique », on ne peut pas avoir un simple texte “=”, mêmes avec des espaces. C’est considéré comme une expression invalide. On peut s’en sortir avec l’expression =”=” mais on perd en lisibilité du rapport.
    • En même temps, lorsque le texte contient de la mise en forme, on n’a pas le mode « Expression unique ». Dans l’exemple suivant, la parenthèse est en gras :
      RDLC - Text Box - Single Expression Disabled
    • On en déduit une astuce pour afficher un “=” sans avoir à coder une expression : il suffit de faire suivre le signe égal par un espace en gras.

TextBox et PlaceHolder

Le cas de figures plus complexe consiste à combiner textes statiques et d’expressions. Chaque expression correspond alors à un “PlaceHolder” :
RDLC - Text Box - Create PlaceHolder

Une fois l’expression saisie, le placeholder créé est généralement symbolisé par un «Expr» dans le texte. Il suffit de double cliquer dessus ou de faire un clic-droit pour le modifier :
RDLC - Text Box - PlaceHolder Menu

Alors, on a la possibilité d’attribuer un Label, ce qui permet d’augmenter encore la lisibilité du rapport :
RDLC - Text Box - PlaceHolder Label

Ce cas mixant textes statiques et expressions est à privilégier par rapport à une expression globale construisant le texte final, du type ="Année = " & Parameters!Periode.Value, toujours en vue d’améliorer la lisibilité et donc la maintenabilité du rapport.

On peut aller encore plus loin en produisant du HTML en sortie d’expression. Ce dernier sera correctement interprété lorsque l’on coche le type de markup « HTML » dans les propriétés du PlaceHolder.

A noter que les expressions avec juste un paramètre sont déjà bien nommées :
RDLC - Text Box - Parameter PlaceHolder Default Label

Tablix

Pour éditer les propriétés d’une tablix, cliquer à l’intérieur pour éditer une cellule puis faire un clic droit dans le coin supérieur gauche :
RDLC - Tablix Corner for Properties Menu

Header/Footer – SubReport

  • Les paramètres globaux Globals!PageNumber et Globals!TotalPages sont utilisables uniquement dans un header ou dans un footer.
  • Il est possible de ne pas imprimer l’entête / le pied de page sur la première et/ou dernière page. Par contre, la place reste réservée sur la page : cela fera juste une zone vide lors de l’impression et le corps de page commencera en dessous. RDLC - Header Properties - Print on first page
  • Quand un rapport est utilisé en tant que subreport, ses header et footer et ses marges sont ignorés – ce sont ceux du rapport principal qui sont utilisés.

Appel C# de génération au format PDF en mode déconnecté

Il est possible de remplir un DataSet depuis le code appelant, sans besoin de connecter le rapport à une DataSource. On peut même DataSource et DataSet à la main directement dans le XML du fichier RDLC :

<DataSources>
  <DataSource Name="DummyDataSource">
    <ConnectionProperties>
      <DataProvider />
      <ConnectString />
    </ConnectionProperties>
    <rd:DataSourceID>28be852c-851b-4e60-8fcc-a6dcbd085955</rd:DataSourceID>
  </DataSource>
</DataSources>
<DataSets>
  <DataSet Name="ListeAssocies">
    <Query>
      <DataSourceName>DummyDataSource</DataSourceName>
      <CommandText />
    </Query>
    <Fields>
      <Field Name="Nom">
        <DataField>Nom</DataField>
        <rd:TypeName>System.String</rd:TypeName>
      </Field>
      <Field Name="Adresse">
        <DataField>Adresse</DataField>
        <rd:TypeName>System.String</rd:TypeName>
      </Field>
    </Fields>
  </DataSet>
</DataSets>

Alors, pour générer le rapport (ici au format PDF), on alimente les données (paramètres et datasets) directement dans le code :

string[] streamids;
string mimeType;
string encoding;
string filenameExtension;

var reportViewer = new ReportViewer();

reportViewer.LocalReport.ReportPath = "..\\..\\Report1.rdlc";

// Paramètres
reportViewer.LocalReport.SetParameters(new ReportParameter("Nom", ""));
reportViewer.LocalReport.SetParameters(new ReportParameter("Adresse", ""));

// DataSet "ListeAssocies"
DataTable tmp = new DataTable();
DataColumn dc1 = tmp.Columns.Add("Nom");
DataColumn dc2 = tmp.Columns.Add("Adresse");

var row = tmp.NewRow();
row.SetField(dc1, "xxx");
row.SetField(dc2, "xxx");
tmp.Rows.Add(row);

row = tmp.NewRow();
row.SetField(dc1, "yyy");
row.SetField(dc2, "yyy");
tmp.Rows.Add(row);

reportViewer.LocalReport.DataSources.Add(new ReportDataSource("ListeIntervenants", tmp));

// Génération au format PDF
byte[] bytes = reportViewer.LocalReport.Render(
    "PDF", null, out mimeType, out encoding, out filenameExtension,
    out streamids, out warnings);

using (FileStream fs = new FileStream("report1.pdf", FileMode.Create))
{
    fs.Write(bytes, 0, bytes.Length);
}

Fenêtres flottantes

Faire attention aux fenêtres flottantes, non dockées, dans Visual Studio. Elles deviennent inutilisables dès qu’on ouvre et referme une des fenêtres de dialogue de propriétés d’un élément dans le RDLC, telles que les Report Properties. C’est comme si ces fenêtres de dialogue, modales, étaient toujours ouverts. La seule solution est alors de redémarrer Visual Studio !

Ressources complémentaires

Microsoft Developer Network