SteveZ
SteveZ

Reputation: 195

How can I run a Dispatcher.Invoke() in a .NET Core 3 class library?

I am developing a WPF/NetCore3.1 application with MVVM. In the view there is a button which is bound to a RelayCommand. The ViewModel is in a different class library than the View. In the ViewModel a timer is started which increments a variable every second and triggers the CanExecuteChanged event of the RelayCommand.

Here is my ViewModel:

public ImportExportViewModel()
{
    MakeOfferCommand = new RelayCommand(MakeOffer, CanMakeOffer);


    Timer t = new Timer(1000);
    t.Elapsed += T_Elapsed;
    t.Start();
}

private void T_Elapsed(object sender, ElapsedEventArgs e)
{            
    ElapsedTime++;

    MakeOfferCommand.RaiseCanExecuteChanged();
}

private void MakeOffer()
{
    // TODO Make Offer
}

private bool CanMakeOffer()
{
    return ElapsedTime < 60;
}

And here the RaiseCanExecuteChanged:

public void RaiseCanExecuteChanged()
{
    var handler = CanExecuteChanged;
    if (handler != null)
    {
        handler(this, new EventArgs());
    }
}

But here I get an InvalidOperationException: the calling thread cannot access this object because the object is owned by another thread.

Normally I would execute a Dispatcher.Invoke() here, but that seems not to exist in .NetCore3.1.

Can anyone tell me how I can still make cross-thread calls?

Upvotes: 3

Views: 2197

Answers (1)

mm8
mm8

Reputation: 169420

You could inject your view model with an IDispatch interface that you implement in each platform:

Interface:

public interface IDispatch
{
    bool CheckAccess();
    void Invoke(Action action);
}

View Model:

public IDispatch Dispatch { get; set; }

private void T_Elapsed(object sender, ElapsedEventArgs e)
{
    if (Dispatch != null && !Dispatch.CheckAccess())
        Dispatch.Invoke(new Action(() => { /* do something */ }));
    ...
}

WPF implementation:

public class WpfDispatch : IDispatch
{
    private readonly Dispatcher _dispatcher;

    public WpfDispatch(Dispatcher dispatcher) =>
        _dispatcher = dispatcher;

    public bool CheckAccess() => _dispatcher.CheckAccess();

    public void Invoke(Action action) => _dispatcher.Invoke(action);
}

Upvotes: 3

Related Questions