2

So.. I've got WPF and MVVM pattern. A thread in the ViewModel is running a task to update an observable collection, so when it adds to the collection I do so like this:

Application.Current.Dispatcher.Invoke((Action) (() => 
{
    _files.Add(toAdd);
}));

which works... the issue is when I have NUnit tests and there is no UI thread as I understand it, so I have to run this first to add the files to the collection without an invoke:

if (Application.Current == null) {
   _files.Add(toAdd);
}));

So just to clarify this is how it looks

if (Application.Current == null) {
   _files.Add(toAdd);
   return;
}));

Application.Current.Dispatcher.Invoke((Action) (() => 
{
    _files.Add(toAdd);
}));

This feels wrong because I'm adding two lines of logic to the viewmodel, one where there is a UI and one purely for testing.

Anyone got any insight as to where my approach is going wrong, or if this is actually acceptable?

Thanks

  • _"This feels wrong because I'm adding to lines of logic to the viewmodel"_ - that's what the VM is for. Are you thinking of the _model_? Otherwise I see nothing wrong with what you are doing –  Jun 04 '16 at 12:11
  • sorry that's my bad, I typo'd there. It's meant to say "two lines of logic", one that only gets instigated when testing which doesn't seem right – trouserboycott Jun 04 '16 at 13:07
  • 2
    I'd suggest you either inject something into your VM that uses the dispatcher at runtime but a 'fake' for unit testing, or looking at [this similar question](http://stackoverflow.com/questions/1106881/using-the-wpf-dispatcher-in-unit-tests). – Charles Mager Jun 04 '16 at 14:08
  • Try passing in a SynchronizationContext to the view model, and invoke it's Post method – Mal Jun 04 '16 at 22:47

1 Answers1

1

My ViewModel requires a IUIThreadHelper in its constructor.

    public MyViewModel(IUIThreadHelper uiThreadHelper)
    {
        if (uiThreadHelper == null)
            throw new ArgumentNullException(nameof(uiThreadHelper));
        this.uiThreadHelper = uiThreadHelper;
    }

IUIThreadHelper looks like:

public interface IUIThreadHelper
{
    void InvokeAction(Action action);
}

At normal running I give it a UIThreadHelper which uses the Application's dispatcher.

In tests I give it a simple fake:

        IUIThreadHelper uiThreadHelper = new Fakes.StubIUIThreadHelper()
        {
            InvokeActionAction = (a) => a(),
        };

So I can simple use it as:

this.uiThreadHelper.InvokeAction(() => header.Children.Add(newChild));
lvoros
  • 312
  • 1
  • 14
  • Do this, but use a SynchronizationContext. Within the UI thread, SynchronizationContext.Current is the DIspatcherSynchronizationContext and references the UI thread. SCs are an abstraction, like the example in this answer, but are used everywhere in the framework. –  Jun 06 '16 at 13:56