Advanced Content Editor
The Advanced Content Editor lets you create PowerPoint shapes directly with C# code, test them live, and save them as snippets in the library. Scripts are executed at runtime using Roslyn (Microsoft C# Scripting).
No Support
The Advanced Content Editor is intended for experienced users with C# knowledge.
PPPTools provides no support for custom scripts.
Faulty scripts can have unexpected effects on the open presentation.
How to Open
- Ribbon → Tools → Advanced Content Editor
- or: in the snippet or action management view via Edit code — opens the selected entry's script directly in the editor
Changed
The editor used to be reachable from the task pane's burger menu ≡ as well. That entry has been removed; use the ribbon button instead. The ribbon button itself has also moved: from the Content Manager group to the Tools group — icon and function are unchanged.
The task pane opens automatically in wide mode.
Interface
| Area | Description |
|---|---|
| Line numbers | Narrow panel to the left of the code field — shows line numbers in sync while scrolling |
| Code field | Enter or paste a C# script |
| Cursor position | Shows current line and column (right side of toolbar: Ln 1 Col 1) |
| Error panel | Shows compiler and runtime errors (appears automatically on error) |
Toolbar
The toolbar is organized into button groups. Buttons with a small arrow ▾ open a dropdown with options.
Run & Save
| Button | Function |
|---|---|
| ▶ Run | Execute the script on the active slide |
| 💾 Save ▾ | Dropdown: save snippet locally or upload as draft to the public library |
Dropdown «💾 Save»:
| Option | Description |
|---|---|
| Save as Snippet | Run script, generate preview image, enter metadata and save locally |
| Upload as Draft | Send snippet as draft to the public library (for admin review and approval) |
Analyse & Code
| Button | Function |
|---|---|
| 📂 Load | Load the code of an existing snippet into the editor |
| 🔍 Analyse ▾ | Dropdown with analysis and code tools |
Dropdown «🔍 Analyse»:
| Option | Description |
|---|---|
| Analyse Code | Statically analyse the script: show recognized @param definitions, compiler warnings and errors in a dialog |
| Generate Code (replace) | Generate ppptools.* code from the selected shape and replace the editor content (only if empty or showing the starter template) |
| Generate Code (append) | Append the generated code to the existing editor content (with a separator comment) |
| Show Nodes | Visualize the key points of the selected shape with colored markers on the slide |
| Reset Editor | Reset code to the starter template and clear the error panel |
Line Numbers & Cursor Position
The narrow panel to the left of the code field automatically shows line numbers — synchronized with vertical scrolling. The current cursor position is shown on the right side of the toolbar:
Ln 12 Col 5
The display updates on every cursor movement.
Available Variables
The following variables are available directly in the script:
| Variable | Type | Description |
|---|---|---|
ppptools |
AdvancedScriptHelper |
All PowerPoint operations (shape creation, selection, Boolean ops, styling …) |
Params |
SnippetParamBag |
Parameter values from the @param dialog (for parameterized snippets) |
No direct COM access
oSlide and aPowerPoint are not available in scripts — all operations go through
ppptools.* methods. This prevents uncontrolled access to the PowerPoint application.
ppptools Methods (Overview)
→ Full reference with parameters and code examples: PPPTools Reference
| Category | Methods |
|---|---|
| Slide | SlideWidth, SlideHeight |
| Create | AddShape, AddRect, AddOval, BuildFreeform, AddPolygon, AddPolyline |
| Selection | GetSelected, GetSelectedRange |
| Boolean ops | Union, Intersect, Combine, Subtract |
| Grouping | GroupSelected, Group |
| Duplicate | Duplicate |
| Z-Order | SendBackward, BringForward, SendToBack, BringToFront |
| Flip | FlipH, FlipV |
| Position | CenterOnSlide, Scale |
| Styling | SetFill, SetGradient, SetLine, HideLine, SetText |
| Asking | AskColor, AskRgb, AskFont, AskLine, AskFill |
| Applying the answer | ApplyColor, ApplyFont, ApplyLine, ApplyFill, ToColor |
| Animations | AddAnimation |
Important Scripting Rules
Roslyn scripts run in a special context. The following rules must be followed to avoid compiler errors:
Use var / dynamic instead of Shape
The type Shape exists in two namespaces simultaneously (Microsoft.Office.Core and Microsoft.Office.Interop.PowerPoint) — this causes a compiler error.
// ❌ Error — ambiguous reference (when Shape types are used mixed)
Shape oRect = ppptools.AddRect(...);
// ✅ Correct
var oRect = ppptools.AddRect(...);
Enums without the PowerPoint. prefix
A script already has the namespaces Microsoft.Office.Core, Microsoft.Office.Interop.PowerPoint,
System, System.Drawing and System.Collections.Generic imported. Enums are therefore written
without a prefix — PowerPoint. is not an alias inside a script.
// ❌ Error CS0103: The name 'PowerPoint' does not exist in the current context
ppptools.SetText(sh, "Title", align: PowerPoint.PpParagraphAlignment.ppAlignCenter);
// ✅ Correct
ppptools.SetText(sh, "Title", align: PpParagraphAlignment.ppAlignCenter);
The same goes for MsoTriState, MsoLineDashStyle and every other Office enum. The PPPTools
namespace, by contrast, is not imported: spelling out a type from it — FD_FillSpec, say —
needs using PPPTools; at the top of the script, or simply var.
Use local functions instead of Action<T>
Action<Shape> fails on the ambiguous type Shape (see above), not on the System namespace — that one is imported. A local function taking dynamic sidesteps the ambiguity:
// ❌ Error
Action<Shape> style = sh => { sh.Fill.ForeColor.RGB = ...; };
// ✅ Correct — local void function
void ApplyStyle(dynamic sh)
{
sh.Fill.ForeColor.RGB = ...;
}
Selection for Boolean operations
List<Shape> cannot be used directly due to the namespace conflict. Select shapes using .Select() instead:
// ✅ Correct
oShape1.Select(MsoTriState.msoTrue); // first shape: Replace = true
oShape2.Select(MsoTriState.msoFalse); // additional shape: Replace = false
ppptools.Union();
dynamic oResult = ppptools.GetSelected(); // retrieve result shape
Setting Colors With a Design Reference
A color sits in a PowerPoint file in one of two ways: as a fixed RGB value, or as a reference to a design color slot — “Accent 1, 40 % lighter”. Only the reference follows a template change. Setting it by hand is awkward, because two properties have to be set in the right order:
// The manual way — and the order is not arbitrary:
// ObjectThemeColor resets Brightness to 0.
sh.Fill.ForeColor.ObjectThemeColor = MsoThemeColorIndex.msoThemeColorAccent1;
sh.Fill.ForeColor.Brightness = 0.4f;
PPPTools carries its own color model for exactly this — the same one behind the format dialogs. A script can use it directly: the script host references the PPPTools assembly.
The short way
using PPPTools; // required for the .ApplyTo(...) notation
var color = FD_ColorValue.Parse("accent1+40");
color.ApplyTo(sh.Fill.ForeColor);
color.ApplyTo(sh.Line.ForeColor);
ApplyTo decides for itself: design reference → ObjectThemeColor + Brightness in the
right order, fixed color → RGB, “automatic” → the target is left untouched. The return
value tells you whether anything was set.
Without using
ApplyTo is an extension method — it only exists with using PPPTools; at the top of the script. To do without, call it in full: PPPTools.FD_ColorApply.ApplyTo(color, sh.Fill.ForeColor);
What fits in the text
| Notation | Meaning |
|---|---|
accent1 … accent6 |
the six accent colors of the design |
lt1 dk1 lt2 dk2 |
Light 1 · Dark 1 · Light 2 · Dark 2 |
hlink folHlink |
Link · followed link |
accent1+40 |
40 % lighter |
accent1-25 |
25 % darker |
#8EAADB |
fixed RGB value — does not follow the template |
auto |
not set; the target stays as it is |
These are the same tokens as in the file format (schemeClr val) and the same ones PPPTools
stores in presets and shape tags. Parse never throws — anything unreadable yields auto.
Building one directly instead of parsing
var a = FD_ColorValue.FromTheme(FD_ThemeSlot.Accent1, 0.4f, null); // reference
var b = FD_ColorValue.FromHex("#8EAADB"); // fixed
var c = FD_ColorValue.FromRgb(System.Drawing.Color.Firebrick); // fixed
var d = FD_ColorValue.Auto; // not set
The null in FromTheme is the design source. Setting needs none — the slot goes into
the file as a reference and PowerPoint resolves it itself. It is only needed when the script
has to know the color itself.
Resolving or reading a color back
var theme = FD_Theme.Snapshot(); // fresh snapshot of the design colors
System.Drawing.Color rgb = color.Resolve(theme); // what the reference yields in this deck
var current = FD_ColorApply.Read(sh.Fill.ForeColor, theme);
if (current.HasThemeRef) { /* the shape follows the template */ }
Inside loops
FD_Theme.Snapshot() reads fifteen colors over COM. If you need a color per iteration — puzzle pieces, Wordclouds, tile walls — use FD_Theme.Recent(): the same snapshot, reused for a quarter of a second.
The rule: store the reference, not the resolved value
Resolve exists for display and calculation, not for storage. Writing the resolved
number into the shape freezes the color — the next template change passes it by. Fixed is
still right where the color is not supposed to follow: brand colors, eyedropper colors,
colors taken from an image.
@param color delivers a fixed value
A parameter of type color or themecolor hands the script a System.Drawing.Color — RGB without a reference. To get a design reference in a snippet, build it in code (FD_ColorValue.Parse("accent1")) or let the token be chosen through an @param enum.
Font, border and fill in one go
The same mechanism exists for whole formats. Every field is optional — whatever stays
null is left untouched:
using PPPTools;
using System.Collections.Generic;
var theme = FD_Theme.Snapshot();
new FD_FontSpec { Family = "Segoe UI", Size = 18f, Bold = true,
Color = FD_ColorValue.Parse("accent2") }
.ApplyToShape(sh, theme);
new FD_LineSpec { Visible = true, Weight = 1.5f, Dash = FD_LineDash.Dash,
Color = FD_ColorValue.Parse("dk1") }
.ApplyToShape(sh, theme);
new FD_FillSpec
{
Kind = FD_FillKind.Gradient,
Angle = 90f,
Stops = new List<FD_GradientStop>
{
new FD_GradientStop(FD_ColorValue.Parse("accent1"), 0f),
new FD_GradientStop(FD_ColorValue.Parse("accent1-30"), 1f)
}
}.ApplyToShape(sh, theme);
Position and transparency of a gradient stop are 0…1, not percentages. ApplyToShape
returns how many properties were actually set — 0 means “nothing requested” or “the target
supports none of it”.
Opening Format Dialogs From a Snippet
So far this was about the model: colors, fonts, borders and fills written as values in the script. A snippet can also open the format dialogs itself and ask the user in the middle of a run — the very same windows the ribbon opens.
The difference to snippet parameters: those are collected before the run, so it must be clear up front what to ask for. Asking mid-run also works for whatever only emerges while computing — per shape found, per group, depending on an intermediate result.
| Question | Answer | Dialog |
|---|---|---|
ppptools.AskColor(title, start, autoCaption) |
FD_ColorValue |
color picker — with design reference |
ppptools.AskRgb(title, start) |
Color? |
color picker — fixed color value |
ppptools.AskFont(title, start, changeMode) |
FD_FontSpec |
font |
ppptools.AskLine(title, start, changeMode) |
FD_LineSpec |
border |
ppptools.AskFill(title, start, changeMode) |
FD_FillSpec |
fill, gradients included |
All arguments are optional. The answer is applied with ApplyColor, ApplyFont,
ApplyLine and ApplyFill — those need no using PPPTools;.
Try it: one shape, four dialogs
The snippet creates a rectangle and asks for fill, border, font and text color one after the other — paste, run, done:
var sh = ppptools.AddRect(120, 120, 320, 140);
ppptools.SetText(sh, "Formatted test shape",
align: PpParagraphAlignment.ppAlignCenter);
var fill = ppptools.AskFill("Fill of the test shape", sh); // start values from the shape
if (fill == null) return; // null = canceled
ppptools.ApplyFill(sh, fill);
var line = ppptools.AskLine("Border of the test shape", sh);
if (line != null) ppptools.ApplyLine(sh, line);
var font = ppptools.AskFont("Font of the test shape", sh);
if (font != null) ppptools.ApplyFont(sh, font);
var textColor = ppptools.AskColor("Text color", "lt1"); // suggestion: Background 1
if (textColor != null) ppptools.ApplyColor(sh, textColor, "text");
ppptools.ShowResult("Done", "Test shape formatted.", "OK", "success");
Order matters
SetText sets font name and size itself (defaults Arial / 12 pt), so it has to come before ApplyFont — otherwise the text overwrites the font just chosen.
For comparison, the same thing with parameters, asked before the run:
// @param font Schrift "Font" default="Segoe UI,20,Bold"
// @param fill Fuellung "Fill" default="gradient:#0070C0,#00B0F0,90"
// @param line Rahmen "Border" default="#003A66,1.5,solid"
// @param color Textfarbe "Text color" default="#FFFFFF"
// @button Create test shape
var sh = ppptools.AddRect(120, 120, 320, 140);
ppptools.SetText(sh, "Formatted test shape", color: Params.GetColor("Textfarbe"),
align: PpParagraphAlignment.ppAlignCenter);
ppptools.ApplyFill(sh, Params.GetFill("Fuellung")); // FillSpec from @param fill
ppptools.SetLine (sh, Params.GetLine("Rahmen")); // LineSpec from @param line
ppptools.SetFont (sh, Params.GetFont("Schrift")); // Font from @param font
The difference is not the effort but the result: AskColor returns the color with its
design reference, @param color only passes on a finished color value.
Which one when?
@param — before the run |
Ask* — during the run |
|
|---|---|---|
| When the question comes | once, in the parameter dialog before the start | as often as the snippet wants, anywhere |
| What can be asked | whatever is known when writing the snippet | also whatever only emerges while computing — per shape found, per group |
| Preset | default="…" in the @param line |
a shape (its values) or the last answer (start) |
| Design reference of a color | lost at the parameter boundary → fixed color value | preserved → FD_ColorValue, and ApplyColor writes it onto the slide |
| Cancel | the run never starts | null — the snippet decides: stop, skip, use a default |
| Readability | every setting sits in the snippet header | the questions sit in the code, spread across the flow |
The two do not exclude each other: a snippet may carry @param lines in its header and
ask again later — the basic setting up front, the exception per shape mid-run.
Asking and applying
var color = ppptools.AskColor("Color for the header", "accent1+40");
if (color == null) return; // canceled
ppptools.ApplyColor(sh, color); // fill (default)
ppptools.ApplyColor(sh, color, "line"); // border
ppptools.ApplyColor(sh, color, "text"); // text color
var font = ppptools.AskFont("Font", sh); // start values read from the shape
if (font != null) ppptools.ApplyFont(sh, font);
var line = ppptools.AskLine("Border", sh);
if (line != null) ppptools.ApplyLine(sh, line);
var fill = ppptools.AskFill("Fill", sh);
if (fill != null) ppptools.ApplyFill(sh, fill);
ApplyFont, ApplyLine and ApplyFill return how many properties were actually set.
null means canceled
All five questions return null when the user closes the dialog without confirming. The Apply methods swallow null silently — they simply do nothing. What a cancel means is therefore up to the snippet: stop (return), skip this one shape (continue), or carry on with a default.
What the dialog opens with
For AskFont, AskLine and AskFill, start takes either a shape — then the dialog
reads its current values as start values — or an earlier answer, in which case the
dialog reopens where the user left it. For AskColor, start is a storage string
("accent1+40", "#1F4E79", "auto").
Together that gives the loop parameters could not express:
FD_FillSpec last = null; // needs using PPPTools; — or just use var
foreach (dynamic sh in ppptools.GetSelectedRange())
{
string name = sh.Name;
object start = (object)last ?? (object)sh;
last = ppptools.AskFill("Fill for " + name, start);
if (last == null) break; // cancel ends the loop
ppptools.ApplyFill(sh, last);
}
Time limit
A script may compute for at most 5 minutes, after which PPPTools stops it. Only computing time counts: while one of these dialogs is open, the clock stands still. So a loop over many shapes may take as long as you need to answer. (The same applies in the snippet gallery, there with 30 seconds of computing time.)
Changing only what is ticked
The third switch on AskFont, AskLine and AskFill is change mode: every single
property may then stay on “unchanged”. Only what the user actually ticks is written, the
rest of the shape stays as it is. Without the switch the dialog returns a complete set of
values.
var only = ppptools.AskFont("What should change?", sh, true);
if (only != null) ppptools.ApplyFont(sh, only);
Reference instead of value
AskColor returns an FD_ColorValue, and ApplyColor writes the design reference as
such onto the shape — the color then follows a template change. Parameters cannot do this:
@param color only passes on a finished color value across the parameter boundary.
For the methods that expect a fixed color (SetFill, SetLine, SetGradient), ToColor
resolves the reference against the presentation design:
var c = ppptools.AskColor("Color", "accent1");
if (c == null) return;
ppptools.ApplyColor(sh, c); // keeps “Accent 1”
ppptools.SetGradient(sh2, ppptools.ToColor(c), Color.White); // fixed value
AskRgb is the same thing one step earlier: it asks for a fixed color value right away.
While the dialog is open
The dialogs open in the center of the screen, not at the mouse pointer — the snippet left the pointer somewhere arbitrary. If a progress indicator is running, it steps aside and returns afterwards with its state intact.
Examples
Each example has its own page with a visual preview, full code, and step-by-step explanation.
Foundation examples (Boolean operations):
| # | Example | Topics |
|---|---|---|
| 1 | Rectangle | ppptools.AddRect, fill color, removing the border |
| 2 | Circle | ppptools.AddOval, center-point calculation, border color |
| 3 | Union | Selection pattern, ppptools.Union(), ppptools.GetSelected() |
| 4 | Square with Hole | ppptools.Combine(), true geometric holes |
| 5 | Gear Wheel | Chaining 3 Boolean ops, Adjustments[1], all tooth counts |
Full parameterized examples (all ppptools methods):
| Example | ppptools Methods |
|---|---|
| Gear Wheel (parameterized) | AddShape, AddOval, GetSelected, GetSelectedRange, Union, Intersect, Combine, SlideWidth/Height |
| Post-it (parameterized) | BuildFreeform, AddRect, GetSelected, Duplicate, Intersect, Subtract, FlipH, SendBackward, SetGradient, HideLine, Group |
| Persona (parameterized) | BuildFreeform, AddOval, AddRect, Group, Scale, SlideWidth/Height |
Workflow
Generate Code from a Selected Shape
The Generate Code (replace) and Generate Code (append) options read the currently selected shape in PowerPoint and produce immediately executable ppptools.* code.
Replace vs. Append:
| Option | Behavior |
|---|---|
| Generate Code (replace) | Editor is empty or shows only the starter template → code is inserted directly, replacing the content |
| Generate Code (append) | Existing code is preserved → new code is appended with a separator comment |
What is generated:
| Shape type | Generated code |
|---|---|
| Rectangle | ppptools.AddRect(...) |
| Oval / Circle | ppptools.AddOval(...) |
| Other AutoShape | ppptools.AddShape(MsoAutoShapeType.xxx, ...) |
| FreeForm (straight segments only) | ppptools.AddPolygon(new float[]{...}, new float[]{...}) |
| FreeForm (with curves) | ppptools.BuildFreeform(...) + AddNodes(...) |
| Group | Recursive code for all members + ppptools.Group(...) |
| Fill | ppptools.SetFill(...) or ppptools.SetGradient(...) |
| Line | ppptools.SetLine(...) or ppptools.HideLine(...) |
| Text | ppptools.SetText(...) |
| Animations | ppptools.AddAnimation(...) + timing |
Ideal workflow
Draw a shape manually in PowerPoint → Generate Code (replace) → the editor shows the exact code
to recreate it → adjust parameters → ▶ Run to test → 💾 Save as Snippet.
Show Nodes
The Show Nodes option places colored markers directly on the slide for each key point of the selected shape.
FreeForm Shapes
For FreeForm shapes, each node is labeled with its coordinates, index, segment type (Line / Curve) and editing type (Corner / Auto).
Color coding:
| Color | Meaning |
|---|---|
| 🟢 Green | Start node (index 0) |
| 🔴 Red | Intermediate nodes |
| 🔵 Blue | Last node |
Standard Shapes (Rectangle, Oval, AutoShape …)
For standard shapes, the 4 corner points (TL / TR / BR / BL) and the center point (C) are displayed:
| Marker | Position | Shape |
|---|---|---|
| TL / TR / BR / BL | Corners | Small blue circle |
| C | Center | Small orange diamond |
An info label above the shape shows name, type and dimensions.
Clean up
Delete the markers after analysis: select all shapes named Node_*, NodeLabel_* or NodeInfo_* and delete them, or use Ctrl+Z to undo.
Analyse Code
The Analyse Code option statically checks the current script and shows the result in a dialog:
- Recognized
@paramdefinitions (name, type, default value) - Compiler warnings
- Compiler errors
Useful to verify that all @param declarations are correct before running the script.
→ More about @param: Parameterized Snippets
Testing a Script
- Enter code in the code field or load an existing snippet via 📂 Load
- Click ▶ Run — the shape appears on the active slide
- If errors occur: read the error panel, fix the code, run again
- Delete test shapes manually (Delete key)
Note
A presentation must be open with an active slide.
Saving as a Snippet
- Click 💾 Save ▾ → Save as Snippet
- The script runs — newly created shapes are detected automatically
- A preview image is generated from the new shapes
- Shapes are deleted after the preview is exported
- Metadata dialog: enter Name*, Category, Tags, Description etc.
- Click Save — the snippet appears immediately in the gallery
Technical Name & Grouping
The metadata dialog also offers Technical Name (target object name on insertion) and Grouping (combine multiple created objects). Details: Save Snippet.
Editing an Existing Snippet
You open an already saved Code snippet not via 📂 Load, but directly from the manager:
- Content Manager → Snippets → Manage, select the snippet
- Click Edit code below the preview image
The editor opens in snippet edit mode with a reduced toolbar:
| Button | Function |
|---|---|
| ▶ Run | Run the script on the active slide (as usual) |
| Save | Overwrites the code.cs of this snippet — same ID, metadata unchanged |
| Rebuild preview | Creates the preview image again — from the current editor code, not from the saved file |
| Close | Back to snippet management, with the snippet selected again |
Snippet's own files
In edit mode ppptools.AssetPath() points to the folder of the edited snippet — bundled images or data lists therefore already work while testing in the editor.
Code snippets only
For fragment and picture snippets Edit code is disabled. Opening it requires a Pro license — just like the editor itself.
Uploading as a Draft to the Public Library
Finished snippets can be sent directly from the editor as a draft to the public library:
- Click 💾 Save ▾ → Upload as Draft
- The script runs and a preview image is generated
- Fill in the metadata dialog (Name*, Category, Tags, Description)
- The snippet is sent to the library server as a Draft
- An admin reviews and publishes the snippet
Draft status
After uploading, the snippet is not yet public — it must first be reviewed and approved by an admin.
Microsoft Documentation
| Topic | Link |
|---|---|
| All shape types (MsoAutoShapeType) | learn.microsoft.com |
| Shapes.AddShape method | learn.microsoft.com |
| Shape object (all properties) | learn.microsoft.com |
| FillFormat (fill) | learn.microsoft.com |
| LineFormat (border) | learn.microsoft.com |