FmgLib.MauiMarkup

Fluent Properties

Every bindable property of every control gets a family of fluent extension methods. Understanding this family — it is always the same four shapes — unlocks the whole library, because the pattern repeats for all controls, including third-party controls processed by the source generator.

The Four Overload Shapes#

For a property like Label.FontSize, the library provides:

C#
// 1. Direct value — the one you use 90% of the time
public static T FontSize<T>(this T self, double fontSize) where T : Label;

// 2. Property builder — bindings, theme/platform/idiom values, dynamic resources
public static T FontSize<T>(this T self,
    Func<PropertyContext<double>, IPropertyBuilder<double>> configure) where T : Label;

// 3. Style setter — used inside Style<T> definitions
public static SettersContext<T> FontSize<T>(this SettersContext<T> self, double fontSize) where T : Label;

// 4. Style setter with builder — bindings etc. inside styles
public static SettersContext<T> FontSize<T>(this SettersContext<T> self,
    Func<PropertySettersContext<double>, IPropertySettersBuilder<double>> configure) where T : Label;

Additionally, properties of type double, Color, Thickness, Rect, etc. get an animation helper (see Animations):

C#
Task<bool> AnimateFontSizeTo(double value, uint length = 250, Easing? easing = null);

You never call shapes 3–4 directly; they light up automatically when you write property calls inside new Style<T>(e => e...) — the same method names work in both contexts.

Shape 1 — Direct Values#

C#
new Label()
    .Text("This is a test")
    .Padding(20)
    .FontSize(30)
    .Center()

Because all methods are generic over T and return T, chaining preserves the concrete control type: after .Text(...) you still have a Label, so Label-only methods remain available.

Shape 2 — The Property Builder (e => e…)#

The lambda overload receives a PropertyContext<TValue> (conventionally named e) and is the gateway to everything that is not a constant value:

Bindings#

C#
new Label().Text(e => e.Path("UserName"));                       // bind to BindingContext.UserName
new Label().Text(e => e.Path("Value").Source(slider));           // bind to another control
new Label().Text(e => e.Path("Price").StringFormat("{0:C}"));    // format
new Entry().Text(e => e.Path("Query").BindingMode(BindingMode.TwoWay));

Full binding capabilities (converters, fallback values, multi-binding, compiled getter bindings) are documented in Property Bindings, Binding Converters, MultiBinding and Compiled Bindings.

App theme values (`#

C#
new Label()
    .TextColor(e => e.OnLight(Colors.Black).OnDark(Colors.Teal))
Method Applies when
OnLight(value) App theme is Light
OnDark(value) App theme is Dark
Default(value) Fallback / unspecified theme

The value updates live whenever the theme changes — the user switching the OS theme, or the app assigning Application.Current.UserAppTheme. This is a real AppThemeBinding, not a one-time check (10.2.1 and later).

Device idiom values (`#

C#
new Label()
    .FontSize(e => e.OnDesktop(80).OnPhone(30).Default(50))

Available: OnPhone, OnTablet, OnDesktop, OnTV, OnWatch, Default.

Platform values (`#

C#
new Label()
    .Margin(e => e.OniOS(new Thickness(0, 20, 0, 0)).Default(new Thickness(0)))

Available: OnAndroid, OniOS, OnMacCatalyst, OnWinUI, OnTizen, Default.

Dynamic resources (`#

C#
new Frame()
    .BackgroundColor(e => e.DynamicResource("CardBackground"))

The property tracks the resource key: replace Resources["CardBackground"] at runtime and the UI updates.

Localization#

C#
new Label().Text(e => e.Translate("Hello"));        // JSON localization
new Label().Text(e => e.TranslateResx("Hello"));    // RESX localization

These are live bindings too — calling Translator.Instance.ChangeCulture(...) re-translates every bound property instantly. See Localization (JSON) and Localization (RESX).

Nesting builders#

Idiom/theme/platform builders compose: each branch can itself take a builder instead of a raw value.

C#
new Label()
    .TextColor(e => e
        .OnLight(Colors.Black)
        .OnDark(l => l.DynamicResource("DarkAccent")))   // dark theme uses a dynamic resource

A nested builder cannot be carried by a theme binding, so this form is resolved once against the theme in effect while the page is built and does not follow later theme changes. Keep the plain value form — .OnLight(x).OnDark(y) — wherever the theme has to switch at runtime.

Shapes 3–4 — Inside Styles#

The exact same method names work inside a Style<T> definition; there they produce XAML Setters instead of touching a live control:

C#
new Style<Button>(e => e
    .TextColor(e => e.OnLight(Colors.White).OnDark(AppColors.Primary))
    .BackgroundColor(AppColors.Primary)
    .FontSize(14)
    .CornerRadius(8)
    .Padding(new Thickness(14, 10)))

See Styling for the complete story.

Convenience Shorthands Worth Knowing#

Beyond 1:1 property mapping, the library adds combined setters:

Method Expands to
.SizeRequest(w, h) / .SizeRequest(size) WidthRequest + HeightRequest
.Margin(10) / .Margin(h, v) / .Margin(l, t, r, b) Margin with a built Thickness
.Padding(...) (same overloads) Padding
.Center(), .FillHorizontal(), .AlignTopRight(), … HorizontalOptions/VerticalOptions combos — see Layout Options
.TextCenter(), .TextTopLeft(), … HorizontalTextAlignment/VerticalTextAlignment combos — see Text Alignment
.GridSpan(column: 2, row: 1) Grid.ColumnSpan + Grid.RowSpan
.AbsoluteLayoutBounds(x, y, w, h) AbsoluteLayout.LayoutBounds without constructing a Rect

There are also small utility extensions on plain types:

C#
"#FF3366".ToColor()          // string → Color (hex, with ToColorFromArgb / ToColorFromRgba variants)

Setting Anything Not Covered#

Two escape hatches guarantee you are never blocked:

InvokeOnElement — run arbitrary code against the control mid-chain:

C#
new Entry()
    .Placeholder("Name")
    .InvokeOnElement(entry => entry.ReturnCommand = someCommand)

Plain C# — since views are just objects, you can always assign afterwards:

C#
var entry = new Entry().Placeholder("Name");
entry.ReturnType = ReturnType.Done;