Object References — Assign, InvokeOnElement, RegisterName
In XAML you name elements with x:Name and reach them from code-behind. In FmgLib.MauiMarkup you capture references inline, while building the tree, without breaking the fluent chain.
Assign(out var …) — the x:Name replacement#
Assign stores the current control in a variable and returns the control, so the chain continues:
public static T Assign<T>(this T self, out T obj) where T : BindableObject;
Capture into a local variable#
new Label().Assign(out var label);
new Entry().Assign(out var entry);
Capture into a field#
Button btnOk;
new Button()
.Text("OK")
.Assign(out btnOk);
Typical use — one control referencing another#
The most common pattern: a control declared earlier in the tree is used by a binding or an event handler of a later control.
this.Content(
new StackLayout()
.Children(
new Slider()
.Assign(out var slider)
.Minimum(1)
.Maximum(20),
new Label()
// bind this label's Text to the slider captured above
.Text(e => e.Path("Value").Source(slider).StringFormat("Slider value: {0}"))
.FontSize(28)
)
);
Or event handlers mutating a sibling:
new StackLayout()
.Children(
new Label().Text("Tap the image twice").Assign(out var label),
new Image()
.Source("dotnet_bot.png")
.SizeRequest(100, 100)
.GestureRecognizers(
new TapGestureRecognizer()
.NumberOfTapsRequired(2)
.OnTapped((s, e) => label.Text = "You tapped 2 times!")
)
)
Ordering caveat#
out var variables must be declared before use in C#. If a control earlier in the tree needs a reference to a control that appears later, declare the variable up front:
Button submit = null!;
this.Content(
new VerticalStackLayout()
.Children(
new Entry()
.Placeholder("Name")
.OnTextChanged((s, e) => submit.IsEnabled = !string.IsNullOrEmpty(e.NewTextValue)),
new Button()
.Text("Submit")
.IsEnabled(false)
.Assign(out submit) // assigns to the pre-declared variable
)
);
Hot reload tip: if a page rebuilds via
Build()(Hot Reload), fields captured withAssignare reassigned on every rebuild — locals declared insideBuild()are the safest choice.
InvokeOnElement — arbitrary code mid-chain#
InvokeOnElement runs any action against the control and continues the chain. Use it for the rare API that has no fluent method (non-bindable properties, method calls, event subscriptions with custom logic):
new ActivityIndicator()
.IsRunning(true)
.SizeRequest(70, 70)
.Center()
.InvokeOnElement(ai => ai.Loaded += (s, e) => CheckLogin())
new CollectionView()
.ItemsSource(items)
.InvokeOnElement(cv => cv.ScrollTo(items.Count - 1, position: ScrollToPosition.End))
It is also the standard way to run conditional configuration inline:
new Button()
.Text("Buy")
.InvokeOnElement(b =>
{
if (user.IsPremium)
b.BackgroundColor = Colors.Gold;
})
RegisterName — the literal x:Name equivalent#
For advanced scenarios that need MAUI's name scope (e.g. certain animations or FindByName interop), RegisterName registers the control in the name scope of a given root element:
this.Content(
new StackLayout()
.Assign(out var root)
.Children(
new Label()
.Text("Named element")
.RegisterName("myLabel", root)
)
);
// later
var label = (Label)((INameScope)NameScope.GetNameScope(root)).FindByName("myLabel");
In everyday code you will not need this — Assign covers 99% of cases with compile-time safety.
Choosing Between Them#
| Need | Use |
|---|---|
| Reference a control elsewhere in the same page | Assign(out var x) |
| Store a control in a field for later use | Assign(out _field) |
| Call a method / set a non-bindable property inline | InvokeOnElement(x => …) |
| Conditional setup inside the chain | InvokeOnElement |
| MAUI name-scope interop | RegisterName |
Related Topics#
- Property Bindings —
Source(control)usesAssigned references - Hot Reload — lifecycle implications for captured references