I'm plannning to submit a PR for Avalonia 12 supporting.
Can I discuss about Avalonia 12's multiple dispatcher?
Avalonia 12 support is being added based on the existing Epoxy.Avalonia11 / Epoxy.Core.Avalonia11 projects.
One migration question needs a design decision: how should Epoxy handle Avalonia 12's dispatcher model?
Avalonia 12 introduced multiple dispatcher support, with one dispatcher per thread. Dispatcher.UIThread is still acceptable for applications, but Avalonia recommends that library/control authors use AvaloniaObject.Dispatcher and Dispatcher.CurrentDispatcher where appropriate, instead of always relying on Dispatcher.UIThread.
Reference:
https://docs.avaloniaui.net/docs/avalonia12-breaking-changes#multiple-dispatchers-support
Current Epoxy behavior:
UIThread.Bind() / UIThread.InvokeAsync() are targetless public APIs.
- Avalonia-specific UI-thread continuation currently uses
Dispatcher.UIThread.
- Exception rethrow paths in async event/command handlers also route through the same targetless UI-thread continuation.
- Relevant files:
src/Epoxy/UIThread.cs
src/Epoxy/Supplemental/UIThreadAwaitable.cs
src/Epoxy.Core/Internal/InternalUIThread.cs
src/Epoxy.Core.Avalonia11/Internal/ContinueOnUIThread.cs
src/Epoxy.Core/Internal/InternalWell.cs
src/Epoxy.Core/Fountain.cs
src/Epoxy.Core/EventBinder.cs
src/Epoxy.Core/Command.cs
Possible directions:
-
Keep using Dispatcher.UIThread for Avalonia 12
- Pros:
- Preserves current public API semantics.
- Smallest implementation change.
- Multiple UI threads are currently still unsupported in Avalonia.
- Cons:
- Does not follow the Avalonia 12 library/control author recommendation.
- Less future-proof for headless, embedded, or virtual-window scenarios.
-
Switch Avalonia 12 code to target-object dispatchers where possible
- Pros:
- Better aligned with Avalonia 12.
- More correct when an
AvaloniaObject is already available.
- Better future compatibility.
- Cons:
- Targetless APIs such as
UIThread.Bind() cannot be fully migrated without changing their semantics.
- Using
Dispatcher.CurrentDispatcher for targetless APIs may bind to the caller's current thread dispatcher, which is not necessarily the intended UI dispatcher.
- May require new internal paths or public overloads.
-
Hybrid approach
- Pros:
- Preserves existing targetless
UIThread.* API behavior by keeping it backed by Dispatcher.UIThread.
- Better aligned with Avalonia 12 when an
AvaloniaObject or control is already available.
- Allows paths like
Fountain / Well / EventBinder to use object-bound dispatching without requiring an immediate public API change.
- Provides a conservative migration path: improve dispatcher correctness for attached-object flows now, and consider public overloads later only if real use cases require them.
- Keeps
Dispatcher.UIThread available as a fallback for existing targetless continuation and exception rethrow paths.
- Cons:
- Epoxy would have two dispatcher semantics in the Avalonia 12 implementation: targetless APIs continue to use
Dispatcher.UIThread, while object-bound flows may use AvaloniaObject.Dispatcher.
- In advanced multiple-dispatcher scenarios, control-bound Epoxy code and later targetless
UIThread.* calls could switch to different dispatchers.
- Ordering, reentrancy, and exception rethrow behavior may become harder to reason about when some continuations are object-bound and others fall back to
Dispatcher.UIThread.
- The implementation and test matrix become more complex because both dispatcher paths and their fallback behavior need to be covered.
- Partial migration could create unclear expectations for users: Avalonia 12 support would be more dispatcher-aware in attached-object flows, but not uniformly dispatcher-aware across all Epoxy APIs.
What direction should Epoxy take for Avalonia 12?
I'm plannning to submit a PR for Avalonia 12 supporting.
Can I discuss about Avalonia 12's multiple dispatcher?
Avalonia 12 support is being added based on the existing
Epoxy.Avalonia11/Epoxy.Core.Avalonia11projects.One migration question needs a design decision: how should Epoxy handle Avalonia 12's dispatcher model?
Avalonia 12 introduced multiple dispatcher support, with one dispatcher per thread.
Dispatcher.UIThreadis still acceptable for applications, but Avalonia recommends that library/control authors useAvaloniaObject.DispatcherandDispatcher.CurrentDispatcherwhere appropriate, instead of always relying onDispatcher.UIThread.Reference:
https://docs.avaloniaui.net/docs/avalonia12-breaking-changes#multiple-dispatchers-support
Current Epoxy behavior:
UIThread.Bind()/UIThread.InvokeAsync()are targetless public APIs.Dispatcher.UIThread.src/Epoxy/UIThread.cssrc/Epoxy/Supplemental/UIThreadAwaitable.cssrc/Epoxy.Core/Internal/InternalUIThread.cssrc/Epoxy.Core.Avalonia11/Internal/ContinueOnUIThread.cssrc/Epoxy.Core/Internal/InternalWell.cssrc/Epoxy.Core/Fountain.cssrc/Epoxy.Core/EventBinder.cssrc/Epoxy.Core/Command.csPossible directions:
Keep using
Dispatcher.UIThreadfor Avalonia 12Switch Avalonia 12 code to target-object dispatchers where possible
AvaloniaObjectis already available.UIThread.Bind()cannot be fully migrated without changing their semantics.Dispatcher.CurrentDispatcherfor targetless APIs may bind to the caller's current thread dispatcher, which is not necessarily the intended UI dispatcher.Hybrid approach
UIThread.*API behavior by keeping it backed byDispatcher.UIThread.AvaloniaObjector control is already available.Fountain/Well/EventBinderto use object-bound dispatching without requiring an immediate public API change.Dispatcher.UIThreadavailable as a fallback for existing targetless continuation and exception rethrow paths.Dispatcher.UIThread, while object-bound flows may useAvaloniaObject.Dispatcher.UIThread.*calls could switch to different dispatchers.Dispatcher.UIThread.What direction should Epoxy take for Avalonia 12?