diff --git a/src/TeamsISO.App/ViewModels/MainViewModel.cs b/src/TeamsISO.App/ViewModels/MainViewModel.cs index b5730de..af785b9 100644 --- a/src/TeamsISO.App/ViewModels/MainViewModel.cs +++ b/src/TeamsISO.App/ViewModels/MainViewModel.cs @@ -346,10 +346,18 @@ public sealed class MainViewModel : ObservableObject, IDisposable // Apply the operator's saved sort preference, if any. ApplySortFromPrefs(); + // Subscribe directly (no ObserveOn) and marshal to the UI thread inside + // the callback via Dispatcher.InvokeAsync. The previous ObserveOn( + // SynchronizationContextScheduler) path captured SynchronizationContext + // .Current at subscribe time — fragile in WPF startup ordering, where + // the UI thread's SyncContext can be in a transitional state during + // App.OnStartup and the captured context never pumps subsequent + // OnNext calls. Direct subscribe + explicit dispatcher marshal is the + // pattern proven by Console.Program.cs (engine emits, consumer marshals). _participantsSub = controller.Participants - .ObserveOn(new SynchronizationContextScheduler( - System.Threading.SynchronizationContext.Current ?? new DispatcherSynchronizationContext(_dispatcher))) - .Subscribe(OnParticipantsChanged); + .Subscribe(snapshot => _dispatcher.InvokeAsync( + () => OnParticipantsChanged(snapshot), + DispatcherPriority.Background)); _alertsSub = controller.Alerts .ObserveOn(new SynchronizationContextScheduler(