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

  1. Defer registration until the associated FrameworkElement is loaded (handle its Loaded event, as the watcher itself does with ElementLoaded).
  2. If the watcher was detached, re-attach (set the AssociatedObject / attach API) before calling RegisterValueChangedHandler again.
  3. Check watcher lifecycle: ensure Attach is called before any Register* call and Detach is not called concurrently.
  4. 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

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


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)