stride3d/stride · error · InvalidOperationException
A dependency object must be attached in order to register a
Error message
A dependency object must be attached in order to register a handler.
What it means
AttachHandler requires the watcher to be attached to a FrameworkElement (frameworkElement != null) before handlers can be registered; otherwise it throws InvalidOperationException 'A dependency object must be attached in order to register a handler.' DependencyPropertyDescriptor.AddValueChanged needs a live DependencyObject as the change source. The watcher is typically attached when the associated element loads (ElementLoaded).
Solutions
- Defer registration until the associated FrameworkElement is loaded (handle its Loaded event, as the watcher itself does with ElementLoaded).
- If the watcher was detached, re-attach (set the AssociatedObject / attach API) before calling RegisterValueChangedHandler again.
- Check watcher lifecycle: ensure Attach is called before any Register* call and Detach is not called concurrently.
- Bind/associate the watcher through XAML or code-behind so attach happens as part of element initialization.
Example fix
// before var watcher = new DependencyPropertyWatcher(); watcher.RegisterValueChangedHandler(prop, OnChanged); // element not attached yet // after var watcher = new DependencyPropertyWatcher(); element.Loaded += (s, e) => watcher.RegisterValueChangedHandler(prop, OnChanged);
Defensive patterns
Strategy: validation
Validate before calling
if (watcher.AssociatedObject == null) throw new InvalidOperationException("Watcher not attached; wait for the element's Loaded event."); Type guard
static bool IsAttached(DependencyPropertyWatcher w) => w?.AssociatedObject is not null;
Try / catch
try { watcher.RegisterValueChangedHandler(prop, handler); }
catch (InvalidOperationException ex) when (ex.Message.Contains("must be attached")) { PendingRegistrations.Add((prop, handler)); } Prevention
- Register handlers in the element's Loaded event, never in constructors.
- Use the watcher's built-in attach-on-load lifecycle.
- Guard re-registration after detach with an attach-state flag.
- In ItemsControl scenarios, re-run registration on each recycle/reload.
When it happens
Trigger: Calling RegisterValueChangedHandler (or AttachHandlers) before the watcher has been attached to its associated element — e.g. before the element's Loaded event fires, after Detach() nulled the element reference, or constructing the watcher and registering immediately in a constructor without waiting for load.
Common situations: Registering handlers in a view-model or control constructor instead of on Loaded; disposing/detaching the watcher and then re-registering without re-attaching; virtualization/recycling in ItemsControls detaching the element before code registers handlers.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- A dependency object must be attached in order to unregister
- Can't change the parent of this folder
- A dependency object is already attached to this instance of
- No handler was previously registered for this dependency pro
- The given {window} is not registered as a clipboard listener
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/6205eb09bbef32d9.
Report an issue: GitHub.
Appendix: source
Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Core/DependencyPropertyWatcher.cs:105
}
private void DetachHandlers()
{
if (handlerRegistered)
{
foreach (var handler in handlers)
{
DetachHandler(handler.Item1, handler.Item2);
}
handlerRegistered = false;
}
}
private void AttachHandler([NotNull] DependencyProperty property, [NotNull] EventHandler handler)
{
if (property == null) throw new ArgumentNullException(nameof(property));
if (handler == null) throw new ArgumentNullException(nameof(handler));
if (frameworkElement == null) throw new InvalidOperationException("A dependency object must be attached in order to register a handler.");
DependencyPropertyDescriptor descriptor;
if (!descriptors.TryGetValue(property, out descriptor))
{
descriptor = DependencyPropertyDescriptor.FromProperty(property, AssociatedObject.GetType());
descriptors.Add(property, descriptor);
}
descriptor.AddValueChanged(AssociatedObject, handler);
}
private void DetachHandler([NotNull] DependencyProperty property, [NotNull] EventHandler handler)
{
if (property == null) throw new ArgumentNullException(nameof(property));
if (handler == null) throw new ArgumentNullException(nameof(handler));
if (frameworkElement == null) throw new InvalidOperationException("A dependency object must be attached in order to unregister a handler.");
DependencyPropertyDescriptor descriptor;
if (!descriptors.TryGetValue(property, out descriptor))View on GitHub (pinned to 96fad776d2)