La modernisation n'est plus une option
Pendant des années, bon nombre d'applications pilotées par modèle ont pu continuer à ressembler à ce qu'elles étaient en 2018. Ce n'est plus le cas. Microsoft modernise l'interface des applications model-driven par vagues successives, en l'alignant sur le système de conception Fluent, et la première vague, le fameux New Look, n'est plus un interrupteur que l'on peut discrètement laisser désactivé. Chaque équipe Dynamics 365 Customer Engagement doit donc se poser une question très concrète : que faire des ressources web personnalisées, des thèmes classiques et des contrôles maison conçus pour l'ancienne interface ?
Cet article est le guide de décision que j'aurais aimé avoir sur chaque projet de modernisation. J'y aborde ce que le nouveau look change réellement, comment les thèmes modernes remplacent les thèmes classiques, la différence entre les code components standard et virtuels (React), comment décider si un contrôle Power Apps component framework (PCF) mérite d'être développé, et comment l'empaqueter et le versionner pour qu'il survive à votre processus ALM. Si vous avez d'abord besoin des bases de PCF, commencez par mon précédent article sur PCF ou par la présentation du Power Apps component framework.
Ce que change le nouveau look
Microsoft présente la modernisation comme une série de vagues. La vague 1, le New Look, apporte des contrôles de champ basés sur Fluent, la barre de commandes flottante, des formulaires, vues et tableaux de bord rafraîchis, ainsi que la grille Power Apps à la place de l'ancienne grille en lecture seule. Selon la documentation de Microsoft, le New Look est obligatoire à partir de la release wave 1 de 2026 et les makers ne peuvent plus revenir à l'apparence classique depuis le concepteur d'applications ; un paramètre d'environnement côté administrateur est encore documenté, mais mieux vaut ne pas compter dessus. La vague 2, la refonte de l'en-tête et de la navigation, ajoute un en-tête d'application moderne, un plan de site simplifié et davantage d'espace de travail sur les formulaires ; elle reste optionnelle pour l'instant. Tous les détails sont décrits dans l'apparence moderne et actualisée des applications model-driven.
En pratique, les composants standard de votre application s'en sortiront bien. Le risque se situe dans tout ce que vous avez construit autour. Avant de basculer quoi que ce soit en production, parcourez cette liste de contrôle dans une copie sandbox de la production :
- Scripts de formulaire qui manipulent le DOM : Tout ce qui utilise document.getElementById, des sélecteurs jQuery ou de l'injection CSS sur le balisage de la plateforme n'a jamais été pris en charge et sera la première chose à casser lorsque ce balisage change. Réécrivez-le avec la Client API.
- Ressources web HTML sur les formulaires : Elles fonctionnent toujours, mais détonnent désormais à côté des contrôles Fluent. Décidez pour chacune s'il faut la restyler, la remplacer par un contrôle PCF ou la supprimer.
- Thèmes classiques : Le nouveau look ne tient pas compte des thèmes classiques. Vos couleurs d'entreprise disparaissent tant que vous ne les recréez pas sous forme de thème moderne.
- Couleurs de graphiques personnalisées : La palette moderne écrase les couleurs personnalisées des graphiques, sauf si vous définissez CustomColorOverride="true" dans le XML du graphique.
- Grilles modifiables et contrôles de grille personnalisés : Les vues utilisent désormais la grille Power Apps. Vérifiez que vos contrôles de grille personnalisés et vos sous-grilles modifiables se comportent toujours comme les utilisateurs s'y attendent, et demandez-vous si la grille Power Apps ne couvre pas déjà le besoin.
Les thèmes modernes remplacent les thèmes classiques
Les thèmes modernes ne se configurent plus dans l'ancienne zone Thèmes. Vous créez une ressource web XML contenant un élément CustomTheme (une couleur de base pour la palette, éventuellement une police, une ressource web pour le logo et les couleurs de l'en-tête), vous faites pointer le paramètre Custom theme definition vers cette ressource web, puis vous publiez. La plateforme génère à partir de votre couleur de base une palette Fluent de 16 teintes. La liste complète des attributs figure dans utiliser des thèmes modernes dans Power Apps.
<!-- Web resource crx_modern-theme.xml (type: Data (XML)) -->
<CustomTheme
basePaletteColor="#0F6CBD"
font="'Segoe UI', sans-serif"
logoWebResource="crx_company-logo"
logoTooltip="CRX Partners">
<AppHeaderColors
background="#0B3A5E"
foreground="#FFFFFF"
backgroundHover="#0E4A78"
foregroundHover="#FFFFFF"
backgroundPressed="#082A45"
foregroundPressed="#FFFFFF"
backgroundSelected="#0D4269"
foregroundSelected="#FFFFFF" />
</CustomTheme>Comme le thème se compose d'une ressource web et d'une définition de paramètre, les deux peuvent être placés dans une solution et passer par votre pipeline comme n'importe quel autre composant. C'est un vrai progrès par rapport aux thèmes classiques, notoirement difficiles à déployer de façon cohérente.
Conseil de terrain
Laissez lockPrimary à sa valeur par défaut (false), sauf si la charte graphique impose la valeur hexadécimale exacte. L'algorithme par défaut optimise la palette générée pour l'accessibilité ; forcer la couleur de base dans l'emplacement principal peut produire des combinaisons qui ne respectent pas les exigences de contraste. Quel que soit votre choix, vérifiez le contraste de l'en-tête pour chaque état des boutons.
Code components standard ou virtuels
Dès que l'apparence est moderne, tout contrôle personnalisé qui dessine encore sa propre interface à l'ancienne saute aux yeux. C'est là que la distinction entre les deux types de code components devient importante :
| Aspect | Contrôle standard | Contrôle virtuel (React) |
|---|---|---|
| Rendu | Reçoit un div conteneur dans init et manipule lui-même le DOM | updateView renvoie un élément React ; la plateforme se charge du rendu |
| React et Fluent | Intégrés au bundle de chaque contrôle si vous les utilisez | Bibliothèques partagées de la plateforme, déclarées dans le manifeste |
| Taille du bundle | Plus volumineux, chaque contrôle embarque sa propre copie | Plus léger, les bibliothèques ne sont pas empaquetées |
| Thèmes modernes | Manuel, via les jetons de thème de fluentDesignLanguage | Les contrôles Fluent v9 reprennent automatiquement le thème de l'application |
| Power Pages | Pris en charge | Non pris en charge pour les contrôles React et les bibliothèques de plateforme |
Deux conséquences passent souvent inaperçues. D'abord, vous ne pouvez pas convertir un contrôle standard en contrôle virtuel en modifiant simplement control-type dans le manifeste ; Microsoft indique clairement qu'il faut créer un nouveau contrôle à partir du modèle React et y reporter votre logique. Ensuite, si le même contrôle doit aussi fonctionner dans Power Pages, un contrôle virtuel n'est pas envisageable aujourd'hui.
Créer un contrôle virtuel avec React et Fluent
La création du projet passe par l'option --framework react de pac pcf init. Le projet généré déclare React et Fluent comme bibliothèques de plateforme : elles sont disponibles au moment du build et fournies par l'hôte à l'exécution (les applications model-driven peuvent charger une version compatible plus récente, par exemple React 17.0.2). Les versions prises en charge et les règles exactes sont décrites sur la page contrôles React et bibliothèques de plateforme.
Initialisation et boucle de développement
Ces commandes créent un contrôle de champ React, l'exécutent dans le banc d'essai local et le poussent vers un environnement de développement pour un premier test dans un vrai formulaire :
# Make sure the Power Platform CLI is current pac install latest # Scaffold a React (virtual) field control and run npm install pac pcf init -n PriorityInput -ns Crx -t field -fw react -npm # Build and open the local test harness (rebuilds on save) npm run build npm start watch # Inner loop only: push to a DEVELOPMENT environment pac auth create --environment https://yourorg-dev.crm4.dynamics.com pac pcf push --publisher-prefix crx
Le manifeste
Les deux différences avec un contrôle standard sont control-type="virtual" et les éléments platform-library. Fluent 8 et Fluent 9 ne peuvent pas être déclarés ensemble dans le même manifeste ; si vous n'utilisez pas Fluent du tout, supprimez cette ligne.
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<control namespace="Crx" constructor="PriorityInput" version="1.0.0"
display-name-key="PriorityInput" description-key="PriorityInput_Desc"
control-type="virtual">
<property name="value" display-name-key="Value" description-key="Value_Desc"
of-type="Whole.None" usage="bound" required="true" />
<resources>
<code path="index.ts" order="1" />
<platform-library name="React" version="16.14.0" />
<platform-library name="Fluent" version="9.46.2" />
</resources>
</control>
</manifest>index.ts et le composant
init ne reçoit pas de conteneur et updateView renvoie un élément React. Gardez index.ts minimal : traduisez le contexte PCF en props, conservez la valeur de sortie courante et appelez notifyOutputChanged. Toute la partie visuelle se trouve dans un composant React ordinaire que vous pouvez tester isolément.
// index.ts
import { IInputs, IOutputs } from "./generated/ManifestTypes";
import { PriorityInputView, IPriorityInputProps } from "./PriorityInputView";
import * as React from "react";
export class PriorityInput implements ComponentFramework.ReactControl<IInputs, IOutputs> {
private notifyOutputChanged: () => void;
private currentValue: number | null = null;
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary
): void {
this.notifyOutputChanged = notifyOutputChanged;
}
public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {
this.currentValue = context.parameters.value.raw;
const props: IPriorityInputProps = {
value: this.currentValue,
disabled: context.mode.isControlDisabled,
onChange: (newValue: number | null) => {
this.currentValue = newValue;
this.notifyOutputChanged();
},
};
return React.createElement(PriorityInputView, props);
}
public getOutputs(): IOutputs {
return { value: this.currentValue ?? undefined };
}
public destroy(): void {
// Nothing to clean up: the platform unmounts the React tree
}
}
// PriorityInputView.tsx
import * as React from "react";
import { SpinButton, SpinButtonOnChangeData } from "@fluentui/react-components";
export interface IPriorityInputProps {
value: number | null;
disabled: boolean;
onChange: (value: number | null) => void;
}
export const PriorityInputView: React.FC<IPriorityInputProps> = ({ value, disabled, onChange }) => (
<SpinButton
value={value}
min={1}
max={5}
disabled={disabled}
onChange={(_event, data: SpinButtonOnChangeData) => {
const parsed = data.value ?? (data.displayValue ? Number(data.displayValue) : null);
onChange(parsed !== null && !Number.isNaN(parsed) ? parsed : null);
}}
/>
);Comme le contrôle utilise des composants Fluent v9 issus de la bibliothèque de plateforme, il partage le contexte React de la plateforme et hérite du thème actif, y compris votre thème moderne personnalisé, sans code supplémentaire. Si vous stylez des éléments qui ne proviennent pas de Fluent, utilisez les jetons de context.fluentDesignLanguage plutôt que des couleurs codées en dur, et relisez-les à chaque appel de updateView afin que les changements de thème soient pris en compte.
Quand un contrôle PCF vaut-il la peine d'être développé ?
Les projets de modernisation génèrent souvent une longue liste de souhaits en matière de contrôles personnalisés. La plupart ne devraient pas être développés. Un code component, c'est du code dont vous êtes responsable : il lui faut un dépôt, un build, une gestion des versions, des tests à chaque release wave et quelqu'un qui le comprenne encore dans trois ans. Ma règle empirique : épuisez d'abord les possibilités de configuration.
Privilégiez la configuration lorsque
- Un contrôle standard existe : La grille Power Apps, l'éditeur de texte enrichi et les contrôles de saisie standard comme toggle, star rating et slider couvrent une part étonnante des demandes.
- Le besoin est la cohérence visuelle : Si une ressource web n'existe que pour afficher des libellés de statut colorés ou l'identité visuelle, un thème moderne, une règle métier ou un formulaire simplifié suffisent souvent.
- Une page personnalisée convient mieux : Pour une interaction guidée en plusieurs étapes, une custom page construite avec des contrôles low-code peut coûter moins cher à maintenir qu'un contrôle sur mesure.
- Il ne sert qu'une fois : Un contrôle présent sur un seul formulaire pour une poignée d'utilisateurs justifie rarement le coût de tout son cycle de vie.
Développez un contrôle PCF lorsque
- L'interaction est réellement réutilisable : Le même modèle de saisie revient sur de nombreux formulaires, tables ou applications.
- Vous remplacez une ressource web HTML : Un contrôle lié à un champ bénéficie d'une vraie liaison de données, respecte l'état du formulaire et le mode lecture seule, et paraît natif dans l'interface moderne, ce qu'un iframe ne fera jamais.
- Les utilisateurs y travaillent toute la journée : C'est sur les écrans très utilisés, où une meilleure saisie ou visualisation fait gagner un temps réel, que l'investissement est rentabilisé.
- L'équipe peut le maintenir : Quelqu'un est responsable du code TypeScript et React, du build et du rythme des livraisons.
Standard ou virtuel ?
Pour tout nouveau contrôle destiné aux applications model-driven ou canevas, partez d'un contrôle virtuel avec Fluent v9. Ne choisissez un contrôle standard que si vous avez besoin de Power Pages ou d'une bibliothèque de rendu qui ne s'inscrit pas dans le modèle React.
Empaquetage, gestion des versions et ALM
pac pcf push est un raccourci de développement. La commande compile le contrôle et le déploie dans une solution temporaire PowerAppsTools (ou dans une solution que vous indiquez avec --solution-unique-name), en contournant les contrôles de version. Parfait pour la boucle de développement, mais ce n'est pas le bon outil pour tout ce qui vient après l'environnement de développement. Pour les environnements de test, d'acceptation et de production, créez un projet de solution (cdsproj) qui référence le contrôle et livrez ce résultat dans votre solution gérée. Les étapes sont décrites dans empaqueter un code component dans un fichier de solution.
# From the control folder: bump the PATCH version in the manifest pac pcf version --strategy manifest # One-time: create a solution project next to the control mkdir Solutions cd Solutions pac solution init --publisher-name CRXPartners --publisher-prefix crx pac solution add-reference --path .. # Release build = production-optimised control bundle dotnet build --configuration Release # or: msbuild /t:restore then msbuild /p:configuration=Release
La configuration Release produit un build de production du contrôle. Attention : pac pcf push et npm run build produisent des builds de développement, sauf si vous définissez l'élément PcfBuildMode sur production dans le fichier pcfproj (ou utilisez npm run build -- --buildMode production). N'importez jamais un build de développement dans les environnements en aval : le bundle est plus volumineux et plus lent. Vérifiez aussi si SolutionPackageType doit valoir Managed, Unmanaged ou Both dans le fichier cdsproj pour votre pipeline.
Des règles de versionnage qui vous épargnent un ticket de support
- Incrémentez au minimum la partie PATCH de la version du contrôle dans ControlManifest.Input.xml à chaque déploiement ; sinon, les applications model-driven risquent de continuer à servir la version précédente mise en cache.
- Utilisez pac pcf version --strategy manifest pour incrémenter le patch, ou pac pcf version --patchversion avec le numéro de build de votre pipeline.
- Gardez le MAJOR.MINOR du contrôle aligné sur le MAJOR.MINOR de la solution qui le livre.
- Assurez-vous que le préfixe d'éditeur utilisé en développement correspond à l'éditeur de la solution qui transporte le contrôle vers les environnements suivants.
- Exécutez le build dans votre pipeline et non sur le portable d'un développeur, afin que chaque artefact soit traçable jusqu'à un commit.
Une seule solution ou une solution de composants séparée ?
Si le contrôle est partagé entre plusieurs applications ou environnements, ou s'il suit son propre rythme de livraison, placez-le dans sa propre solution et faites-en dépendre les solutions applicatives. L'ordre d'importation devient alors important : la solution de composants doit être installée en premier. Si le contrôle n'appartient qu'à une seule application, le garder dans la solution de celle-ci est plus simple. Microsoft décrit les deux approches dans les recommandations ALM pour les code components.
Pièges fréquents
- Oublier d'incrémenter la version : L'importation réussit, rien ne change à l'écran et toute l'équipe perd un après-midi à vider des caches.
- Embarquer React malgré tout : Importer une autre version de React ou un package Fluent complet dans un contrôle virtuel annule tout l'intérêt. Restez dans les versions prises en charge par la plateforme.
- Ignorer les états désactivé et masqué : Lisez context.mode.isControlDisabled et les informations de sécurité au niveau du champ ; un contrôle qui permet de modifier des données en lecture seule est un bug, pas une fonctionnalité.
- Couleurs codées en dur : Elles jurent avec le thème moderne du client et avec les futures options d'affichage. Utilisez des composants Fluent v9 ou des jetons de thème.
- Livrer des builds issus de pac pcf push : La solution PowerAppsTools est non gérée et n'est pas destinée aux environnements en aval.
- Ne tester que dans le banc d'essai : Le banc d'essai local est excellent pour itérer rapidement, mais validez toujours dans un vrai formulaire avec le nouveau look, sur ordinateur et dans l'application mobile si les utilisateurs s'en servent.
Conclusion
Moderniser une application model-driven consiste surtout à retirer des éléments : scripts DOM non pris en charge, thèmes classiques, ressources web qui se battent contre l'interface Fluent. Configurez le thème moderne, appuyez-vous sur les contrôles standard et réservez PCF aux interactions réutilisables qui apportent une vraie valeur. Lorsque vous développez, créez des contrôles virtuels sur les bibliothèques React et Fluent de la plateforme, empaquetez-les via un projet de solution et intégrez la gestion des versions à votre definition of done. C'est ainsi que votre application gardera un aspect natif au fil des prochaines vagues, au lieu de redevenir datée.