
We know since the beginning that the PCF properties have a “default-value” which allows us to define a default configuration value for the properties in model-driven apps. It is documented that this is allowed only on input properties, since the bound parameters expect to have a column associated. I think they are interesting only on not required properties, since the required ones will need a column associated and won’t apply the default value.
As first thought, pfx-default-value seemed to me the same as default-value, only that it applies on Canvas Apps (and Custom Pages). But it’s more, since we can provide there PowerFx expressions. They allow us to evaluate events and properties using PowerFx expressions, instead of static default values. Here is a link to the docs, explaining how to use pfx-default-value.
One use case was my last blog, where I could use pfx-default-value to detect that the PCF is running inside a Canavs App. Today I would like to provide colors from a PCF in order to use the theming tokens from Fluent 9 on Canvas controls . The PCFs don’t have a type “Color” for the property types. We can provide string codes, and the makers need to user ColorValue() on top, in order to use the color. Even if this solution won’t be able to break those limits, it can make the life of the maker easier.
But first., let’s see how pfx-default-value works.
Using pfx-defalt-value for parameters of type input
We can use it on properties or events. Let’s consider making a PCF which would provide colors from Theming. The CanvasApp has only access to the base ramp (16 base colors) used by the Theming in Fluent UI 9. But the Fluent UI Theming 9 provides tokens for a lot of colors, well adjusted to the theming colors. My best practice is to never use hard-coded colors. The PCF gets the complete Fluent 9 Theming provided in the context, and we want to make it available to the Canvas App through a PCF.
We’ll define a property called “colorName” of type input, where we’ll ask the name of the color to be returned. This property is of type “input” and we provide the pfx-default-value as colorStatusWarningForeground1:
Since I want to provide the string for pfx-default-value, I’m wrapping the string (with double quotes) in quotes.
As a result we get the pfx-default-value set as soon we use the PCF
What happens if we update the pfx-default-value?
If we change the pfx-default-value of a PCF, the value won’t be changed. There it doesn’t matter if the value was changed by the maker, or it is the older pfx-default-value. The new value will apply only when we use a new instance of the PCF.
Example:
PCF version 0.0.1 has a pfx-default-value = “colorStatusWarningForeground1”. We use it on a CanvasApp, but we don’t change the value.
Then we change the PCF pfx-default-value to “colorStatusWarningForeground3“, increment the PCF version and update the PCF on the CanvasApp.
Then the old instances of the PCFs are using the old values: the default from the past (colorStatusWarningForeground1) or the ones changed by the user. But a new instance of the PCF will use the new value: colorStatusWarningForeground3.
pfx-default-value and hidden properties
We can even define the properties as hidden=”true”. That way we can provide some logic to the PCF, using some PowerFx possibilities, while the maker doesn’t even know what the PCF is doing.
Just take care that in case you change the pfx-default-value later, the existing PCFs on the Canvas will keep the old value, and the maker won’t notice what’s happening.
The result
We change back the property on hidden=”false” and implement a little the PCF. We’ll use the colorName to get that color from the Theming:
this.colorCode =
this.context.fluentDesignLanguage?.tokenTheme
?.[this.colorName ?? "colorBrandForeground1"]
And we return that from the PCF
public getOutputs(): IOutputs {
return {
colorCode: this.colorCode
};
}
We use the PCF a few times, with different properties
- ThemingColorsBackground1:
Control: CodeComponent
ComponentName: diana_Dianamics.ThemingColors
Properties:
colorName: ="colorNeutralBackground1"
And now the same for the background, foreground and for the border (of course it’s not a good solution to have a PCF per color, but that’s only for demo purposes for now):
| PCF Name | colorName |
|---|---|
| ThemingColorsBackground1 | colorNeutralBackground1 |
| ThemingColorsBackground2 | colorStatusSuccessBackground2 |
| ThemingColorsBackground3 | colorStatusWarningBackground2 |
| ThemingColorsForeground1 | colorNeutralForeground1 |
| ThemingColorsForeground2 | colorStatusSuccessForeground2 |
| ThemingColorsForeground3 | colorStatusWarningForeground2 |
| ThemingColorsBorder1 | colorBrandForeground2 |
| ThemingColorsBorder2 | colorStatusSuccessBorder1 |
| ThemingColorsBorder3 | colorStatusWarningForeground2 |
We define 3 Rectangles on the Canvas, with some icons on it:
- Rectangle1:
BorderColor: =ColorValue(ThemingColorsForeground1.colorCode)
Fill: =ColorValue(ThemingColorsBackground1.colorCode)
- Icon1:
IconColor: =ColorValue(ThemingColorsForeground1.colorCode)
- Rectangle2:
BorderColor: =ColorValue(ThemingColorsBorder2.colorCode)
Fill: =ColorValue(ThemingColorsBackground2.colorCode)
- Icon2:
IconColor: =ColorValue(ThemingColorsForeground2.colorCode)
- Rectangle3:
BorderColor: =ColorValue(ThemingColorsBorder3.colorCode)
Fill: =ColorValue(ThemingColorsBackground3.colorCode)
- Icon3:
IconColor: =ColorValue(ThemingColorsForeground3.colorCode)
And a text control like this :
- TextInputCanvas1_2:
Appearance: ='TextInputCanvas.Appearance'.FilledDarker
BasePaletteColor: =ColorValue(ThemingColorsForeground3.colorCode)
In the standard blue theming we get the following:
And we get this using red theme
Unchanged in the green theme
Conclusion: We could change the theme, but we were able to keep the colors for the rectangle, icons and text input control using different colors, well adjusted to all themes. That way we can provide a consistent definition for some behavior like error, warning, success.. but we can take also some special colors (just search for palette in the list of the Fluent UI 9 colors)
Dark mode
Until now it’s maybe not that impressive. The colors for Warning and Success are well adjusted with the theming colors, but they never change. We could basically grab the codes from the Fluent UI 9 colors list, and use them in the app.
Right now dark mode is not provided by the Canvas Apps Modern Theming , and my hope is that some day we’ll get that option. But just for fun, let’s see what happens if we extend the PCF with the option to return darkMode. We’ll use the brand colors from the main theme, which is provided through the PCF context fluentDesignLanguage:
createDarkTheme(
this.context.fluentDesignLanguage?.brand as BrandVariants) as any
)?.[this.colorName ?? "colorBrandForeground1"]
We’ll use the PCF a few times on the canvas. We’ll define the following (all with the isDarkMode true). It will look something like this
- ThemingColorsBackground1Dark:
Control: CodeComponent
ComponentName: diana_Dianamics.ThemingColors
Properties:
colorName: ="colorNeutralBackground1"
isDarkMode: =true
Here is the list of all darkmode controls and properties used:
| Control | colorName |
|---|---|
| ThemingColorsBackground1Dark | “colorNeutralBackground1” |
| ThemingColorsBackground2Dark | “colorStatusSuccessBackground2” |
| ThemingColorsBackground3Dark | “colorStatusWarningBackground2” |
| ThemingColorsForeground1Dark | “colorNeutralForeground1” |
| ThemingColorsForeground2Dark | “colorStatusSuccessForeground2” |
| ThemingColorsForeground3Dark | “colorStatusWarningForeground2” |
| ThemingColorsBorder1Dark | “colorBrandForeground2” |
| ThemingColorsBorder2Dark | “colorStatusSuccessBorder1” |
| ThemingColorsBorder3Dark | “colorStatusWarningForeground2” |
Then I define similar controls with the ones in light mode.. but this time with the isDarkMode option.
The text control definition is this time using a “success” base color:
- TextInputCanvas1_1:
Control: TextInput@0.0.54
Properties:
Appearance: ='TextInputCanvas.Appearance'.FilledDarker
BasePaletteColor: =ColorValue(ThemingColorsBorder2Dark.colorCode)
Fill: =ColorValue(ThemingColorsBackground1Dark.colorCode)
FontColor: =ColorValue(ThemingColorsForeground1Dark.colorCode)
Placeholder: ="Hello Dark Theme"
Width: =378
X: =32
Y: =88
In Blue theme, we get the following light and dark colors:
Now the same in the red theme:
I don’t think all the controls are ready for dark mode (at least not the modern controls), but if we have a consistent color definition extracted from the theming, we’ll be able to use dark mode when all controls are ready.
Using pfx-default-value with events
Right now, we were only able to provide the colorCode as output property of type LingleLine.Text. This means that we need to use “ColorValue()” over and over again. We are not able to provide a property of Color as an output type from the PCF. But we can use events, and for the events we can provide pfx-default-value too. So we try to use this to automatically make the ColorValue() call.
We’ll add the event to the manifest
The pfx-default-value is able to access all the variables, controls and screens in the app; including Self. Of course this solution is not the best with the PCF design until now. Since I can define only one variable, and cannot dynamically adjust it. But let’s see how it works:
To trigger the event I have this method. To get the darkMode we just use the createDarkTheme method, and provide the base colors from the PCF context:
private changeColor = () => {
this.events.push("OnColorChange");
this.colorCode = this.isDarkMode===false
? this.context.fluentDesignLanguage?.tokenTheme?
.[this.colorName ?? "colorBrandForeground1"]
: (createDarkTheme(this.context.fluentDesignLanguage?.brand as BrandVariants) as any)?
.[this.colorName ?? "colorBrandForeground1"];
this.notifyOutputChanged();
}
And the getOutputs is changed like this
public getOutputs(): IOutputs {
if(this.events.length > 0){
window.setTimeout(() => {
this.context.events.OnColorChanged();
this.events = [];
}, 100);
}
return {
colorCode: this.colorCode
};
}
I was told that this approach to provide event related data is not the best one. The better approach would be to call notifyOutputChange(), which will call the updateView again, so we can trigger the event in updateView, if the events array is not empty. In my test that didn’t work every time (and it has to be triggered in the beginning, when we don’t have a real change in the parameters). So I’ll stick with the implementation using the code above, which worked quite well.
Now I can use the PCF, which automatically implements the OnColorChange event. The user doesn’t have to implement the event.
And now I can fill a rectangle using the varColorWarningForeground; no ColorValue() call conversion needed:
Of course I could change the OnColorChanged event for every PCF used until now.. but of course we’ll use another approach if we want to use the PCF for theming in a more consistent way.
ThemingColors PCF
Using this approach I’ve created a PCF which hopefully can help to overcome the limitations of the modern Theming as implemented today.
So I’ve created the ThemingColors PCF (you can find it in my github repository).
In this PCF, I have an “OutputTheme” property, which provides all the theming tokens from Fluent UI 9, so you also have access to all colors. Here is the list with all theming tokens.
The downside of using the OutputTheme is that the colors are provided as strings. But the PCF also provides an event “OnColorChanged“, where the main colors are extracted in a variable “ExtendedTheme”, with the colors already parsed. But this is only the default; if you want to add more colors, or change something, you just need to change the “OnColorChanged”, defining your own PowerFx expression.
So all you need to do, is to add the PCF on your form (that will be a transparent rectangle) and just use the ExtendedTheme variable. You can choose if you want the colors in DarkMode, with an additional property of the PCF: isDarkMode.
So for creating some kind of error/success indicator, we could use something like the following (the following is the “Danger” container, and I’ve took only the relevant parts from the code yaml)
- ContainerDanger:
Children:
- Rectangle3_5:
BorderColor: =ExtendedTheme.ErrorForeground
Fill: =ExtendedTheme.ErrorBackground
- Icon1_4:
Icon: ="ErrorCircle"
IconColor: =ExtendedTheme.ErrorForeground
- Label2_1:
Color: =ExtendedTheme.ErrorForeground
Text: ="Danger"
In the following
- for “Clock” I’m using App theming colors, like “App.Theme.Colors.Lighter80″
- for Danger I’m using ExtendedTheme.ErrorBackground, ExtendedTheme.ErrorForeground
- for Warning I’m using ExtendedTheme.WarningBackground and ExtendedTheme.WarningForeground
- for Success ExtendedTheme.SuccessBackground and ExtendedTheme.SuccessForegroud
And this is the result in darkmode (the first one – the clock, won’t change since is using the App.Theming colors)
Defining additional colors
There are some patterns to override tokens in Fluent 9 theming, but when I work inside the PCF with theming, there are too many colors. I actually need only the primary color and two accent colors, and then the success, error, warning colors. So I’ve designed the PCF to provide 2 more base colors:
- accent1 : as Background, Foreground and Border
- accent2: as Background, Foreground and Border
These properties are available as input properties for the PCF:
The default properties are Peach and LightTeal (but you can pick as you wish). The values could be from the list of the tokens from the Fluent 9 Theming, and here you see the peach and lightteal accent in light mode:
And this is the result in dark mode
And in the teams theme we get:
Also you could use color codes, but in that case, the color won’t be adjusted if you switch to darkmode.
These properties are also available in the OutputTheme property of the PCF, and in the ExtendedTheme variable. That way you have the primary, accent1 and accent2 colors directly available.
Conclusion
“pfx-default-value” are awesome, since they allow us to implement the code also in PowerFx and reduce complexity for the maker. It can also serve as quick documentation for the maker, since it’s available directly there, where is needed.
It could be also a way to introduce standards in your organizations, since each instance of the PCF will have that default definition.
Regarding Theming and colors: I’m very curious if the ThemingColors PCF will help you handle the missing features in the modern theming for now. In case the standard Theming will allow to do more in the future, we can remove this PCF from the from, and define the colors in the ExtendedTheme variable (or whatever name you’ve chosen), without having to change the rest of the App. In case there are more features needed, please send me a note. I would be interested to hear about the features you need.
Photo by Pixabay: https://www.pexels.com/photo/assorted-color-paints-and-paint-brushes-459278/
Marketing
Project Operations
Supply Chain
HR

