The right place to register event handlers in an MVVM application with BootStrapper ServiceLocator?

Where is the recommended place to register event handlers to propagate events from the hardware layer to the data layer to the UI layer? The situation is as follows:

  • Application in C # /. NET
  • The MVVM pattern is used
  • Bootstrapper implemented using ServiceLocator implemented within Microsoft Unity

Simplified, there are three layers separated in assemblies:

  • UI.dll (UI layer containing ViewModel and View)
  • Data.dll (model layer)
  • HardwareAbstraction.dll (hardware level)

The entry point is the starter.exe file containing the loader and the ServiceLocator configuration. Other libraries use this ServiceLocator. UI.dll then gets the data interface from the ServiceLocator.

The ViewModel needs to display some data as soon as the data comes from the hardware. Dataflow: Scanner like hardware -> HardwareAbstraction.dll -> Data.dll -> UI.dll. From one dll to the next, the event is fired to notify the listener when new data has arrived.

Where is the best place to register event handlers for these events?

At the moment there is no real concept in the application for this, a lot has been done in the constructors, but it doesn't seem right to me. Having them in the constructor often breaks my unit tests (injecting unit tests in an existing application) and design time ViewModels (in C #, not Xaml).

+3


source to share


1 answer


In general, I try to avoid global service locators. I think this is an anti-pattern. Don't use it inside your view models. Testing the bear will do that. Just pass the dependencies in the constructor. I have one main IoC container for each application: my main loader. Then I have some with much less registrations in some factories. I never display a container. This plan came after years of struggling with missing and outdated dependencies.

The viewModel layer must use the PropertyChanged and ObservableCollection to expose its events. Nobody should register for these programmatically if you can help him; they should all be used in bindings at the UI level. Hardware abstraction is another form of model level object. This is the (source) of the data. You can have multiple layers of data / model.



Registering for events at the model level in the constructor (viewModel or model manager) is the right thing to do. Empty them in Dispose. One thing I did to make it cleaner is to fire an event in the model whenever someone registers. It looks like this:

class ModelManager: IModelManager {
  ...
  private Action<IModel> _modelAdded = delegate {};
  public event Action<IModel> ModelAdded {
     add { 
        _modelAdded = Delegate.Combine(_modelAdded, value);
        foreach(var model in Models)
           value.Invoke(model);
     }
     remove { ... }
  }
  ...
}

class ModelManagerVM: ViewModelBase {
   public ModelManagerVM(IModelManager mgr, IModelVMFactory factory) {
      _factory = factory;
      _mgr = mgr;
      _mgr.ModelAdded += OnModelAdded; // triggers on subscribe
   }
   private void OnModelAdded(IModel model) { // never virtual
      // use a dispatcher to push this to the UI thread
      var existing = _modelVMs.FirstOrDefault(m => m.ID == model.ID);
      if (existing != null) existing.Model = model;
      else _modelVMs.Add(_factory.Create(model));
   }
   public void Dispose() { 
      _mgr.ModelAdded -= OnModelAdded; // always unsubscribe on passed-in deps
   }
}

      

+1


source







All Articles