Skip to main content
Configure automatic screen and interaction capture for .NET MAUI applications using the Userpilot MAUI SDK. The Userpilot MAUI SDK can automatically capture screen views, user interactions, and selected presentation events without adding explicit tracking calls to each view. Auto Capture runs in the MAUI wrapper and forwards captured events to the native Userpilot SDK on Android and iOS. No source generator or navigator observer is required — after setup, the SDK attaches to the current MAUI windows and listens to navigation and supported control events.

SDK Configuration

Auto capture is off by default. Pass UserpilotOptions to UserpilotSdk.Setup(...). The same options object contains the platform SDK options and the MAUI auto capture options.
Call setup once after the app has created a MAUI application/window — for example from a startup, splash, or initialize screen. If setup runs before any window is available, call UserpilotAutoCapture.ResumeAutoCapture() after the first window/page is available so the wrapper can attach to the current windows.
NoteAuto Capture automatically tracks screen views and ignores manually sent screen events while enabled.If you are migrating from manual screen tracking, please review the migration guide here.

Screen Capture

When EnableScreenAutoCapture is true, the SDK emits $screen for MAUI navigation and page lifecycle changes: Screen titles are resolved in this order:
  1. Current page Title
  2. Current Shell route/location
  3. Current page type name
Duplicate screen emissions for the same page fingerprint are throttled for a short window.
Manual screensUserpilotSdk.Screen("<title>") is ignored while screen auto capture is enabled. To send screens manually, leave EnableScreenAutoCapture = false.

Interaction Capture

When EnableInteractionAutoCapture is true, the SDK captures the following MAUI interactions: Each interaction maps to a high-level analytics category (tap, text_change, selection_change, value_change, view_presented) — that is the event name that reaches your analytics backend. Text fields are debounced and never send typed text. Text-field payloads contain has_text, text_length, read_only, and obscured. Continuous controls such as Slider and Stepper are debounced so drag/update bursts send one settled event. Every debounced interaction — text changes plus Slider/Stepper values — is flushed before a screen transition and before its control or page is torn down, so it is emitted ahead of the new screen event and keeps the previous screen in its hierarchy. Payloads are snapshotted at interaction time, so a flush that runs after the page has left its parent still reports the hierarchy the user actually interacted with.

Tab Selection

tab_selected uses the shared selection fields, so a tab switch is described like any other selection: DatePicker and TimePicker are driven entirely by the MAUI change event, so their capture timing follows the platform. On Android the dialog commits the value synchronously when OK is tapped, so the event always lands. On iOS the picker is a wheel (UIDatePickerStyle.Wheels) and MAUI commits Date/Time from UIDatePicker.ValueChanged, which UIKit raises only once the wheel settles — see Limitations.

List Selection

CollectionView and ListView both emit list_item_selected with the shared selection fields, so one dashboard rule matches either control. target_class distinguishes them (CollectionView / ListView). Both text fields come from the item’s own ToString(), so give list items a meaningful override. Two gates control this:
  • target_text needs EnableInteractionTextCapture (on by default) and honours RedactText.
  • selected_value needs EnableInteractionValueCapture (off by default) and is not affected by RedactText.
On a list holding sensitive items, RedactText alone is not enough once value capture is on. Use IgnoreInteractions="True" to drop the event entirely, or leave EnableInteractionValueCapture off. ListView is deprecated by MAUI (obsolete from Microsoft.Maui.Controls 10) and is captured for apps ported from Xamarin.Forms. Two differences from CollectionView:
  • Capture is driven by ItemTapped, so it works with SelectionMode="None" — the pattern where a row only navigates — and a programmatic SelectedItem assignment is not captured. CollectionView uses SelectionChanged, which does report programmatic selection.
  • Re-tapping the already-selected row emits another event, because it is another tap. CollectionView emits nothing, because the selection did not change.
With IsGroupingEnabled="True", selected_index and entry_count are resolved across the flattened rows, so they describe the row’s position among all rows rather than MAUI’s rendered row index, which counts group headers.

Component Value Gating

By default, user-input values are stripped from event payloads. Position and configuration metadata such as selected_index, entry_count, min, and max can still be sent because they describe control structure, not the selected value. Set EnableInteractionValueCapture = true to include value fields: Text input content is not captured even when value capture is enabled.

Presentation Capture

Modal pages pushed with Navigation.PushModalAsync(...) emit view_presented with: For custom overlays such as bottom sheets, mark the overlay view with PresentationSurface. The SDK emits view_presented when the marked view changes from hidden to visible:
PresentationTitle is used for target_text and either dialog_title or bottom_sheet_title when text capture is enabled.

Privacy Controls

Global flags govern what is captured:
  • Text captureEnableInteractionTextCapture
  • Accessibility label captureEnableInteractionAccessibilityLabelCapture
  • Value payload captureEnableInteractionValueCapture
Disabling text capture removes text-derived properties from the payload. Redaction replaces target_text with ****; accessibility fields, hierarchy attributes, placeholders, and captured values are not masked by redaction. Auto capture never sends typed input values verbatim — only field metadata such as has_text and text_length. Add the Userpilot namespace to XAML before using attached properties:

UserpilotAutoCapture.RedactText

Redaction replaces the captured target_text with ****. The interaction event still fires — the control is still identified by type, position, and structure — only the human-readable text is masked.
Use RedactText="False" on a descendant to override a redacted ancestor:

UserpilotAutoCapture.IgnoreInteractions

Ignoring skips interaction events for a subtree. The app’s own handlers still run — only auto capture ignores those interactions. Route/page-driven screen events are not suppressed.
Use IgnoreInteractions="False" on a descendant to override an ignored ancestor.

View Tag and Accessibility Value

Enrich events without changing visible UI:
ViewTag is always included when set and is not gated by accessibility capture.

Runtime Controls

Auto capture starts automatically from Setup (based on options). To pause or resume collection at runtime without re-passing the config — for example a “private mode” — use:
StopAutoCapture() and Stop() are aliases for pause. ResumeAutoCapture() resumes using the options supplied during setup. While paused, no screen or interaction events are recorded. Pausing auto capture does not enable manual Screen() calls: when EnableScreenAutoCapture is configured, manual and automatic screen tracking remain mutually exclusive. Privacy markers only affect Userpilot auto capture. They do not disable MAUI controls, commands, gestures, or app event handlers.

Limitations

  • Shell bottom tabs — Captured as $screen navigation, not as tab_selected. TabbedPage emits tab_selected.
  • Native DisplayAlert, DisplayActionSheet, DisplayPromptAsync — Not captured as view_presented. The button that opened them is captured.
  • Bottom sheets — MAUI has no built-in bottom sheet. Mark custom sheet overlays with PresentationSurface.
  • ToolbarItem, MenuItem, context menus, SwipeView actions — Not captured, except MenuFlyoutItem.
  • Scroll gestures, pull-to-refresh, pan, pinch, swipe, pointer, drag — Not captured.
  • WebView, Map, GraphicsView, Skia/custom drawn surfaces — Not captured internally.
  • Programmatic value changes — The SDK listens to MAUI events. Text changes are focus-gated; other control change events may be captured if the control raises its normal change event.
  • iOS DatePicker / TimePicker dismissed before the wheel settles — Not captured. UIKit raises UIDatePicker.ValueChanged only after the wheel settles, and MAUI commits Date/Time from that event (UpdateMode.Immediately, the default) or from the Done button, both of which read the picker’s already-settled value. Tapping Done — or dismissing the picker — before the wheel settles leaves the property unchanged, so MAUI raises no change event and the host app keeps the old value too. Android’s dialog commits synchronously on OK, so the event always lands there.