Cysharp / R3

The new future of dotnet/reactive and UniRx.
MIT License
2.27k stars 99 forks source link

Updating ReactiveProperty bound with x:Bind from non-UI thread in WinUI 3 causes System.Runtime.InteropServices.COMException #254

Closed southernwind closed 2 months ago

southernwind commented 2 months ago

I am encountering an issue in a WinUI 3 application where updating a ReactiveProperty that is bound to the UI using x:Bind from a non-UI thread results in a System.Runtime.InteropServices.COMException.

Steps to Reproduce:

  1. Create a ReactiveProperty in a ViewModel.
  2. Bind the ReactiveProperty to a UI element using x:Bind.
  3. Update the ReactiveProperty from a non-UI thread (e.g., using Task.Run).

Example Code:

public class ReactivePropertyViewModel {
    public ReactiveProperty<int> Count { get; } = new();

    public ReactiveCommand IncrementInTaskCommand { get; } = new();

    public ReactivePropertyViewModel()
    {
        this.IncrementInTaskCommand.Subscribe(() => {
            Task.Run(() =>
            {
                this.Count.Value++;
            });
        });
    }
}

Expected Behavior: The ReactiveProperty should be updated without causing any exceptions.

Actual Behavior: A System.Runtime.InteropServices.COMException is thrown when the ReactiveProperty is updated from a non-UI thread.

Any guidance on how to resolve this issue would be greatly appreciated. Thank you!

southernwind commented 2 months ago

You can find a minimal reproducible example in the following repository: https://github.com/southernwind/R3WinUITest

neuecc commented 2 months ago

ReactiveProperty does not automatically assignment to UI Threads, so you must control it yourself. The most commonly used is ObserveOn, For example

IncrementInTaskCommand./* Operators */.ObserveOnCurrentSynchronizationContext().Subscribe(x => { } )

would be written like this, and the inside of the Subscribe would be processed on the UI thread.

southernwind commented 2 months ago

Thank you. This issue has been resolved.