Initial : marketplace + 2 plugins (syncfusion-ribbon-accent, offscreen-winforms-render)
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://code.claude.com/docs/plugins/plugin-schema.json",
|
||||
"name": "syncfusion-ribbon-accent",
|
||||
"description": "Couleur d'accent personnalisée (live) sur un ruban Syncfusion WinForms Office2016 — ruban, backstage (dont le survol des items), onglets de contenu (TabControlAdv), grilles SfDataGrid. Donne le bon levier par élément et les pièges (ThemeStyle ignoré, ThemeName à conserver, BackStageItemHoverColor, FlattenHierarchy…). Réf. testée : Syncfusion WinForms 33.2.x.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Julien Cluzel"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": "https://repos.ara-it.info/ARA-IT/ara-claude-plugins"
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
name: syncfusion-ribbon-accent
|
||||
description: À utiliser pour IMPLÉMENTER ou DÉBOGUER une couleur d'accent personnalisée (live, choisie par l'utilisateur) sur une IHM Syncfusion WinForms Office2016 — ruban (RibbonControlAdv), backstage (BackStage/menu Fichier, y compris le SURVOL des items), onglets de contenu (TabControlAdv) et grilles (SfDataGrid). Donne le bon levier par élément et les pièges (ThemeStyle ignoré, ThemeName à ne pas vider, BackStageItemHoverColor, FlattenHierarchy…). Réf. testée : Syncfusion WinForms 33.2.x.
|
||||
---
|
||||
|
||||
# Couleur d'accent sur un ruban Syncfusion WinForms (Office2016)
|
||||
|
||||
**But** : faire suivre une **couleur d'accent libre** à TOUTE l'IHM (ruban, backstage, onglets, grilles), en live.
|
||||
**Testé** : Syncfusion Essential Studio **WinForms 33.2.10**, .NET 10, C#.
|
||||
**Prérequis** : `SfSkinManager.LoadAssembly(typeof(Office2016Theme).Assembly);` au démarrage.
|
||||
|
||||
## Règle d'or — chaque zone a SON mécanisme (ne pas les confondre)
|
||||
|
||||
| Élément | Bon levier (33.2.x) | À NE PAS faire |
|
||||
|---|---|---|
|
||||
| **Ruban** (bandeau, onglets, Fichier, boutons système) | `ribbon.Office2016ColorTable` (**Clear + Add d'UNE table**), en **gardant** `ThemeName="Office2016"` | `ribbon.ThemeStyle` (ignoré en live) ; vider `ThemeName` (ruban plat « touch ») |
|
||||
| **Backstage — titre + bouton retour ←** | `ribbon.ThemeStyle.BackStageStyle` (`TitleBarBackColor`, `ControlBoxBackColor`…), **ré-assigner** `ribbon.ThemeStyle` | — |
|
||||
| **Backstage — items (onglets+boutons) repos/SURVOL/sélection** | `ribbon.Office2016ColorTable.BackStageItem*` (le renderer `BackStage2016` lit cette table) | `BackStageButton.BackColor`/`MetroColor`/`OverrideFormManagedColor`/`ThemeStyle` (écrasés par le renderer) |
|
||||
| **Backstage — onglet actif** | `backStage.ActiveTabColor` / `ActiveTabForeColor` | — |
|
||||
| **Onglets de contenu** (`TabControlAdv`) | `content.ActiveTabColor` / `ActiveTabForeColor` | — |
|
||||
| **Grilles** (`SfDataGrid`) | surcharge directe de `grid.Style` | chercher un `ThemeSettings` (inexistant en 33.2.10) |
|
||||
| **QuickAccess Toolbar** (retrait) | `ribbon.QuickPanelVisible=false` + `ribbon.ShowQuickItemsDropDownButton=false` | — |
|
||||
|
||||
**3 principes** : (1) **garder** `ribbon.ThemeName="Office2016"` (le vider casse le rendu) ; (2) `Office2016ColorTable` est une `BindingList` **sans clé** → **une seule table** (`Clear()`+`Add()`) ; (3) le renderer Office2016 lit cette table pour le ruban **ET** la backstage.
|
||||
|
||||
## Pièges (à ne pas redécouvrir)
|
||||
|
||||
1. `ribbon.ThemeStyle` n'a **aucun effet live** sur le bandeau du ruban (le skin repeint par-dessus) → `Office2016ColorTable`.
|
||||
2. Vider `ribbon.ThemeName` colore le bandeau MAIS retire le skin → rendu plat « touch », laid. **Garder `"Office2016"`.**
|
||||
3. `Office2016ColorTable` = `BindingList` sans clé/scheme → **une seule entrée** suffit.
|
||||
4. **Survol des items backstage** : ni `BackStageButton.BackColor`/`MetroColor`/`OverrideFormManagedColor`+`CustomManagedColor`/`Appearance=Metro`/`ButtonAdvVisualStyle.HoverBackColor` — le renderer peint et lit la **color table** → **`BackStageItemHoverColor`**.
|
||||
5. `backStage.ThemeName` se fait **réécraser** par SfSkinManager (`BackStage2016Renderer`) ; le vider est inopérant.
|
||||
6. **Noms `[Obsolete]`** : `BackStage*Tab*Color`/`BackStage*Button*Color` → unifiés en **`BackStageItem*`**.
|
||||
7. `SfSkinManager` 33.2.10 = `LoadAssembly` + `SetTheme` seulement (pas de `ThemeSettings`/`RegisterThemeSettings`).
|
||||
8. `Office2016ColorTable` ne s'applique que sous `Office2016ColorScheme.Colorful`.
|
||||
9. **Réflexion incomplète = fausse conclusion** : lister les propriétés d'un type Syncfusion SANS `BindingFlags.FlattenHierarchy` rate les **membres hérités** (piège qui avait fait conclure à tort que `BackStageItemHoverColor` n'existait pas). **Toujours `Public|Instance|FlattenHierarchy`** + signaler les `[Obsolete]`.
|
||||
|
||||
## Vérification (environnement sans bureau / agent)
|
||||
|
||||
- **Rendre le VRAI form** offscreen (un mock minimal ne reproduit PAS le bleu backstage). `PrintWindow` (flag 2) + thread STA → PNG relisible + `bmp.GetPixel`. Cf. méthode de rendu offscreen.
|
||||
- Ouvrir la backstage par réflexion : `backStageView.ShowBackStage()` (champ privé).
|
||||
- **Forcer un survol** pour le capturer sans souris : `btnX.State = ButtonAdvState.MouseOver;` puis `Invalidate()`.
|
||||
- Injecter une **couleur de test distinctive** (orange/rouge/violet), pas l'accent par défaut, pour distinguer « appliqué » de « inchangé ».
|
||||
- **Vérifier l'API par réflexion AVANT de coder** (`MetadataLoadContext`, `FlattenHierarchy`).
|
||||
|
||||
## Checklist d'intégration
|
||||
|
||||
1. `SfSkinManager.LoadAssembly(typeof(Office2016Theme).Assembly)`.
|
||||
2. Ruban : `RibbonStyle.Office2016` + `Office2016ColorScheme.Colorful` + `ThemeName="Office2016"` + `CanOverrideStyle=true` + `MenuColor=accent`.
|
||||
3. **Une** `Office2016ColorTable` (chrome=accent, corps clair, `BackStageItem*`=accent) → `Clear()`+`Add()`.
|
||||
4. `ThemeStyle.BackStageStyle` : `TitleBarBackColor`/`ControlBoxBackColor` (+hover/pressed) → ré-assigner `ThemeStyle`.
|
||||
5. `backStage.ActiveTabColor` + `content.ActiveTabColor`.
|
||||
6. Grilles : `grid.Style.*` (sélection/en-tête/bordure/ascenseurs) + ré-application live.
|
||||
7. QAT retirée si inutile.
|
||||
8. Helpers `ForeColorFor` / `Shade` / `Tint`.
|
||||
9. **Vérifier au rendu offscreen** (vrai form + survol forcé) avant de conclure.
|
||||
|
||||
## Implémentation de référence (copiable)
|
||||
|
||||
```csharp
|
||||
using Syncfusion.Windows.Forms; // BackStage, BackStageButton
|
||||
using Syncfusion.Windows.Forms.Tools; // RibbonForm, RibbonControlAdv, Office2016ColorTable, TabControlAdv
|
||||
using Syncfusion.WinForms.Controls; // SfSkinManager
|
||||
using Syncfusion.WinForms.DataGrid; // SfDataGrid
|
||||
|
||||
internal static class AppTheme
|
||||
{
|
||||
public static Color AccentColor { get; set; } = ColorTranslator.FromHtml("#0099CC");
|
||||
public const string SfThemeName = "Office2016Colorful";
|
||||
|
||||
// === RUBAN + BACKSTAGE (titre/bouton retour) ===
|
||||
public static void ApplyOffice2016(RibbonForm form, RibbonControlAdv ribbon, Color accent)
|
||||
{
|
||||
ribbon.RibbonStyle = RibbonStyle.Office2016;
|
||||
ribbon.Office2016ColorScheme = Office2016ColorScheme.Colorful;
|
||||
ribbon.ThemeName = "Office2016";
|
||||
ribbon.CanOverrideStyle = true;
|
||||
ribbon.MenuColor = accent;
|
||||
|
||||
ribbon.Office2016ColorTable.Clear();
|
||||
ribbon.Office2016ColorTable.Add(BuildOffice2016ColorTable(accent));
|
||||
|
||||
Color fore = ForeColorFor(accent);
|
||||
var themeStyle = ribbon.ThemeStyle;
|
||||
var bs = themeStyle.BackStageStyle;
|
||||
bs.TitleBarBackColor = accent; bs.TitleBarForeColor = fore;
|
||||
bs.ControlBoxBackColor = accent; // bouton retour ←
|
||||
bs.HoverControlBoxBackColor = Shade(accent, 0.12);
|
||||
bs.PressedControlBoxBackColor = Shade(accent, 0.20);
|
||||
bs.ItemBackColor = accent; bs.ItemForeColor = fore;
|
||||
bs.SelectedItemBackColor = Shade(accent, 0.20); bs.SelectedItemForeColor = fore;
|
||||
bs.HoverItemBackColor = Shade(accent, 0.12); bs.HoverItemForeColor = fore;
|
||||
ribbon.ThemeStyle = themeStyle;
|
||||
|
||||
ribbon.Refresh();
|
||||
ribbon.Invalidate(true);
|
||||
}
|
||||
|
||||
private static Office2016ColorTable BuildOffice2016ColorTable(Color accent)
|
||||
{
|
||||
Color fore = ForeColorFor(accent);
|
||||
Color body = ColorTranslator.FromHtml("#F1F1F1");
|
||||
Color bodyText = ColorTranslator.FromHtml("#575757");
|
||||
Color border = ColorTranslator.FromHtml("#D2D2D2");
|
||||
Color hover = ColorTranslator.FromHtml("#C5C5C5");
|
||||
return new Office2016ColorTable
|
||||
{
|
||||
// Chrome à l'accent
|
||||
HeaderColor = accent, TabBackColor = accent, TitleColor = fore,
|
||||
TabForeColor = fore, HoverTabForeColor = fore, SelectedTabColor = accent,
|
||||
CheckedTabColor = body, CheckedTabForeColor = accent,
|
||||
FileMenuForeColor = fore, FileMenuHoverForeColor = fore,
|
||||
SystemButtonBackground = accent, ControlBoxForeColor = fore,
|
||||
CloseButtonBackground = ColorTranslator.FromHtml("#E81123"),
|
||||
// Items backstage (le renderer lit cette table) — SURVOL = BackStageItemHoverColor
|
||||
BackStageItemForeColor = fore,
|
||||
BackStageItemHoverColor = Shade(accent, 0.16),
|
||||
BackStageItemHoverForeColor = fore,
|
||||
BackStageItemSelectionColor = Shade(accent, 0.26),
|
||||
BackStageItemSelectionForeColor = fore,
|
||||
BackStageTitleColor = ColorTranslator.FromHtml("#3A3A3A"),
|
||||
// Corps clair, lisible
|
||||
CaptionBackColor = body, CaptionForeColor = bodyText,
|
||||
ToolStripBackColor = body, PanelBackColor = body, RibbonPanelBorderColor = border,
|
||||
ToolStripItemForeColor = bodyText, ToolStripSpliterColor = border,
|
||||
ButtonBackgroundHovered = hover, ButtonBackgroundSelected = hover,
|
||||
ButtonBackgroundPressed = ColorTranslator.FromHtml("#969696"),
|
||||
};
|
||||
}
|
||||
|
||||
// === ONGLETS DE CONTENU ===
|
||||
public static void ApplyContentTabs(TabControlAdv content, Color accent)
|
||||
{
|
||||
content.ActiveTabColor = accent;
|
||||
content.ActiveTabForeColor = ForeColorFor(accent);
|
||||
content.Refresh(); content.Invalidate(true);
|
||||
}
|
||||
|
||||
// === BACKSTAGE : onglet actif (le reste via la color table) ===
|
||||
public static void ApplyBackStageItems(BackStage backStage, Color accent)
|
||||
{
|
||||
backStage.ActiveTabColor = accent;
|
||||
backStage.ActiveTabForeColor = ForeColorFor(accent);
|
||||
backStage.Invalidate(true);
|
||||
}
|
||||
|
||||
// === GRILLES ===
|
||||
public static void ApplySfControlTheme(Control control) => SfSkinManager.SetTheme(control, SfThemeName);
|
||||
|
||||
public static void ApplyGridAccent(SfDataGrid grid, Color accent)
|
||||
{
|
||||
grid.Style.SelectionStyle.BackColor = Tint(accent, 0.55);
|
||||
grid.Style.SelectionStyle.TextColor = Color.Black;
|
||||
grid.Style.HeaderStyle.SortIconColor = accent;
|
||||
grid.Style.HeaderStyle.FilterIconColor = accent;
|
||||
grid.Style.BorderColor = accent;
|
||||
grid.Style.VerticalScrollBar.ThumbColor = Tint(accent, 0.45);
|
||||
grid.Style.VerticalScrollBar.ThumbHoverColor = accent;
|
||||
grid.Style.HorizontalScrollBar.ThumbColor = Tint(accent, 0.45);
|
||||
grid.Style.HorizontalScrollBar.ThumbHoverColor = accent;
|
||||
grid.Invalidate();
|
||||
}
|
||||
|
||||
public static void RefreshGridAccents(Control root, Color accent)
|
||||
{
|
||||
foreach (Control c in root.Controls)
|
||||
{
|
||||
if (c is SfDataGrid g) ApplyGridAccent(g, accent);
|
||||
if (c.HasChildren) RefreshGridAccents(c, accent);
|
||||
}
|
||||
}
|
||||
|
||||
// === HELPERS ===
|
||||
private static Color ForeColorFor(Color c)
|
||||
=> (0.299 * c.R + 0.587 * c.G + 0.114 * c.B) / 255.0 > 0.55 ? Color.Black : Color.White;
|
||||
public static Color Tint(Color c, double t) => Color.FromArgb(c.A,
|
||||
(int)(c.R + (255 - c.R) * t), (int)(c.G + (255 - c.G) * t), (int)(c.B + (255 - c.B) * t));
|
||||
private static Color Shade(Color c, double f) => Color.FromArgb(c.A,
|
||||
(int)(c.R * (1 - f)), (int)(c.G * (1 - f)), (int)(c.B * (1 - f)));
|
||||
}
|
||||
```
|
||||
|
||||
### Câblage (au démarrage ET dans le picker de couleur)
|
||||
|
||||
```csharp
|
||||
AppTheme.ApplyOffice2016(this, ribbon, accent);
|
||||
AppTheme.ApplyContentTabs(content, accent);
|
||||
AppTheme.ApplyBackStageItems(backStage, accent);
|
||||
AppTheme.RefreshGridAccents(content, accent); // grilles déjà ouvertes
|
||||
// QAT : ribbon.QuickPanelVisible = false; ribbon.ShowQuickItemsDropDownButton = false;
|
||||
// + persister la couleur et la relire au démarrage
|
||||
```
|
||||
|
||||
> Guide long + récit complet : `docs/syncfusion-ribbon-couleur-accent.md` (dans le repo d'origine CapToilettage).
|
||||
> Technique de vérification : `docs/methode-rendu-offscreen.md`.
|
||||
Reference in New Issue
Block a user