Suppose I have a Windows WPF application (SampleApp) that is using IoC (Ninject). I have wired up everything in my composition root as follows.
[STAThread]
static void Main()
{
IKernel kernel = new StandardKernel();
var modules = new List<NinjectModule>
{
new ExternalDependencies(),
new DependencyResolver()
};
kernel.Load(modules);
var mainWindow = kernel.Get<IMainWindow>();
mainWindow.ShowDialog();
kernel.Release(mainWindow);
kernel.Dispose();
}
The Conents of ExternalDependencies can be ignored for this example, but here are the contents of DependencyResolver which is for internal dependencies.
class DependencyResolver : NinjectModule
{
public override void Load()
{
Bind<IMainWindow>().To<MainWindow>().InSingletonScope();
Bind<IProductWindow>().To<Product>().InSingletonScope();
}
}
As you can see, I have MainWindow and Product wired up. The ctor on MainWindow looks as follows.
public MainWindow(IDomain<TaskFields, TaskExtFields, TaskEmbeds> task)
{
_task = task;
InitializeComponent();
}
We're all good to this point. However, now I am at the point where I would like to have actions in the main window (button clicks, menu selections, etc) open various other forms. For example I have a Product window as follows and I want a button click to open the window and wire everything up appropriately.
public Product(IDomain<ProductFields, ProductExtFields, ProductEmbeds> productDomain)
{
_productDomain = productDomain;
InitializeComponent();
}
Looking to keep my IoC from leaking out of the Main method. In other words I don't want the code to look like...
var productWindow = GlobalIoC.Get<IProductWindow>();
which would violate the three R's principle, require me to make the IoC global and leak information that I hope is not needed out into the rest of the code.
Right now I am dealing with 2 or 3 windows for testing - however in a final application I can see this growing to a larger number, say perhaps hundreds of windows each with their own backing viewmodels (when I get through this I will be implementing viewmodels), domain, data models, repositories, etc.
I have all of this working for the main form but I cannot wrap my head around how to properly wire everything up so that it's available, and stays in the composition root.
To be clear, this is currently a learning project (IoC, MVVM, DI)
Suggestions?
This is how the solution partially looks like.
Since I am using the Onion Architecture in a Winforms environment, hence I have the UI, Infrastructure and Core layers. All the layers are loosely coupled using Dependency Injection. What I want to achieve is that whenever a form from e.g. Accounts Forms (Class Library) is loaded, all the dependencies for that should be loaded in the UnityContainer i.e. types registered. These dependencies are interfaces and implementations present in Core and Infrastructure projects.
My confusion is that where should I write the code to register dependencies? What would be the Composition Root for this application? Please note that forms from e.g. Accounts Forms, HR Forms, etc are all loaded using reflection in the Main Windows application which references only the Base Forms Project.
After Eben Roux's suggestions
Here is how I am executing the wireup code when the assembly is loaded:
Dim assemb As System.Reflection.Assembly
...
...
If assemb IsNot Nothing Then
Dim type As Type = GetType(IDependencyWiring)
Dim modules As List(Of Type) = assemb.GetTypes().Where(Function(p) type.IsAssignableFrom(p) AndAlso p.IsClass).ToList()
For Each [module] As Type In modules
Dim argTypes As Type() = New Type() {}
Dim cInfo As ConstructorInfo = [module].GetConstructor(argTypes)
Dim dependencyWiringModule As IDependencyWiring = DirectCast(cInfo.Invoke(Nothing), IDependencyWiring)
dependencyWiringModule.WireUp()
Next
End If
Here's is the Module having the WireUp method:
Public Class AccountModule : Implements IDependencyWiring
Private Shared Container As IUnityContainer
Public Sub New()
Container = New UnityContainer()
End Sub
Public Sub WireUp() Implements IDependencyWiring.WireUp
Container.RegisterType(Of IInterface1, Class1)()
Container.RegisterType(Of IInterface2, Class2)()
Container.RegisterType(Of IInterface3, Class3)()
Container.RegisterType(Of IInterface4, Class4)()
End Sub
Public Shared Function Resolve(typeToResolve As Type) As Object
Return Container.Resolve(typeToResolve.GetType())()
End Function
End Class
So my questions now are:
Is it the right approach to store the Container as Shared and use it to resolve dependencies via Resolve method?
There is a problem in the way I am encapsulating the Resolve behavior of the Container. What would be the right syntax for that? I don't want to reference Unity on each of the form to be able to call the Resolve method, so I am encapsulating that i my own Resolve method. In this way I could easily replace the AccountModule with another one if I want to change the IOC Container without having the change the container references everywhere.
With this type of plug-in architecture you effectively end up with more than one composition root (of sorts). There will in all probability be some dependencies only your plug-in knows about and can wire up.
So part of your architecture should be the loading of the plug-ins. This probably happens somewhere in the main application in the wire-up bit (composition root) which would then give each plug-in the opportunity to perform its wiring.
Since not all plug-ins may need wiring one could make that explicit by using a seperate interface:
public interface IDependencyWiring
{
public void WireUp(IDependencyContainer container); // <-- changed to conform to update
}
And then in the main composition root:
foreach (var plugin in plugins)
{
var wiring = plugin as IDependencyWiring;
if (wiring != null)
{
wiring.WireUp(myContainer);
}
}
I hope that makes sense.
Update:
Firstly I would use a safe cast. TryCast in the VB.NET world. You could use dependency iversion to get rid of Unity from the actual plug-ins by using your own interface. Something like so:
public interface IDependencyContainer
{
void Register(Type type);
void Register<T>();
void Resolve(Type type);
void Resolve<T>();
}
Well, you would add what you need. Then pass in the reference to the container in the wire-up as I did up top public void WireUp(IContainer container);.
The Resolve behahiour is somewhat problematic in that you seem to be headed in a Service Locator direction. Try to get as much of the resolving done by the container by using constructor (or other) injection. Of course this works fine for Singleton components. For the Transient ones I'd rather use a Singleton factory that receives an instance of the IDependencyContainer (so that will also be registered), and it does the resolving (creation, really) for you.
See the next post. This original one question content has been removed, as doesn't have any sense. Briefly, I asked how to bind XML (which I generated by mistake while parsing DLL assembly) to TreeView using XmlDataProvider in MVVM way. But later I understood that this approach was wrong, and I switched to generation of data entity model (just write classes which represent all the entities I would like to expose in the tree) instead of XML.
So, the result in the next post. Currently from time to time I update this "article", so F5, and
Enjoy reading!
Introduction
The right way I had found reading this article
It's a long story, most of you just can skip it :) But those, who want to understand the problem and solution, must read this all !
I'm QA, and some time ago had become responsible for Automation of the product I clicks. Fortunately, this automaton takes place not in some Testing Tool, but in Visual Studio, so it is maximally close to development.
For our automation we use a framework which consist of MbUnit (Gallio as runner) and of MINT (addition to MbUnit, which is written by the customer we work with). MbUnit gives us Test Fixtures and Tests, and MINT adds additional smaller layer -- Actions inside tests. Example. Fixture is called 'FilteringFixture'. It consist of amount of tests like 'TestingFilteringById', or 'TestingFilteringWithSpecialChars', etc. Each test consist of actions, which are atomic unit of our test. Example of actions are - 'Open app (parameter)', 'OpenFilterDialog', etc.
We already have a lot of tests, which contain a lot of actions, it's a mess. They use internal API of the product we QA. Also, we start investigation a new Automation approach - UI automation via Microsoft UI Automation (sorry for tautology). So the necessity of some "exporter", or "reporter" tool became severe for managers.
Some time ago I have got a task to develop some application, which can parse a DLL (which contains all the fixtures, tests and actions), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). But, right after that, I went to vacation (2 weeks).
It happens so, that my girlfriend went to her family until vacation (she also got it), and I remained at home so alone. Thinking what me to do all this time (2 weeks), I remember about that "write exporter tool task" and how long I have been planning to start learning WPF. So, I decided to make my task during vacation, and also dress a application to WPF. At that time I heard something about MVVM, and I decided to implement it using pure MVVM.
DLL which can parse DLL with fixrtures etc had been written rather fast (~1-2 days). After that I had started with WPF, and this article will show you how it ended.
I have spent a major part of my vacation (almost 8 days!), trying to sorted it out in my head and code, and finally, it is done (almost). My girlfriend would not believe what I was doing all this time, but I have a proof!
Sharing my solution step by step in pseudo code, to help others avoid similar problems. This answer is more looks like tutorial =) (Really?). If you are interested what were the most complicated things while learning WPF from scratch, I would say -- make it all really MVVM and f*g TreeView binding!
If you want an archived file with solution, I can give it a bit later, just when I have made a decision, that it is worth of that. One limitation, I'm not sure I may share the MINT.dll, which brings Actions, as it has been developed by the customer of our company. But I can just remove it, and share the application, which can display information about Fixtures and Tests only, but not about actions.
Boastful words. With just a little C# / WinForms / HTML background and no practice I have been able to implement this version of the application in almost 1 week (and write this article). So, impossible is possible! Just take a vacation like me, and spend it to WPF learning!
Step by step tutorial (w/o attached files yet)
Short repetition of the task:
Some time ago I have got a task to develop an application, which can parse a DLL (which contains test fixtures, test methods and actions - units of our unit testing based automation framework), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). I decided to implement it using WPF and pure MVVM (both were absolutely new things for me). The 2 most difficult problems for me became MVVM approach itself, and then MVVM binding to TreeView control. I skip the part about MVVM division, it's a theme for separate article. The steps below are about binding to TreeView in MVVM way.
Not so important: Create DLL which can open DLL with unit tests and finds fixtures, test methods and actions (more smaller level of unit test, written in our company) using reflection. If you are interested in how it had been done, look here: Parsing function / method content using Reflection
DLL: Separated classes are created for both fixtures, tests and actions (data model, entity model?).We'll use them for binding. You should think by yourself, what will be an entity model for your tree. Main idea - each level of tree should be exposed by appropriate class, with those properties, which help you to represent the model in the tree (and, ideally, will take right place in your MVVM, as model or part of the model). In my case, I was interested in entity name, list of children and ordinal number. Ordinal number is a number, which represents order of an entity in the code inside DLL. It helps me show ordinal number in the TreeView, still not sure it's right approach, but it works!
public class MintFixutre : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintTest> _tests = new List<MintTest>();
public MintFixutre(string fixtureName, int ordinalNumber)
{
_name = fixtureName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal number must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintTest> Tests
{
get { return _tests; }
}
public string Name { get { return _name; }}
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintTest : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintAction> _actions = new List<MintAction>();
public MintTest(string testName, int ordinalNumber)
{
if (string.IsNullOrWhiteSpace(testName))
throw new ArgumentException("Test name cannot be null or space filled");
_name = testName;
if (ordinalNumber <= 0)
throw new ArgumentException("OrdinalNumber must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintAction> Actions
{
get { return _actions; }
}
public string Name { get { return _name; } }
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintAction : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
public MintAction(string actionName, int ordinalNumber)
{
_name = actionName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal numbers must begins from 1");
_ordinalNumber = ordinalNumber;
}
public string Name { get { return _name; } }
public bool IsParent { get { return false; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
BTW, I also created an interface below, which implement all the entities. Such interface can help you in the future. Still not sure, should I was also add there Childrens property of List<IMintEntity> type, or something like that?
public interface IMintEntity
{
string Name { get; }
bool IsParent { get; }
int OrdinalNumber { get; }
}
DLL - building data model: DLL has a method which opens DLL with unit tests and enumerating data. During enumeration, it builds a data model like below. Real method example is given, reflection core + Mono.Reflection.dll are used, don't be confused with complexity. All that you need - look how the method fills _fixtures list with entities.
private void ParseDllToEntityModel()
{
_fixutres = new List<MintFixutre>();
// enumerating Fixtures
int f = 1;
foreach (Type fixture in AssemblyTests.GetTypes().Where(t => t.GetCustomAttributes(typeof(TestFixtureAttribute), false).Length > 0))
{
var tempFixture = new MintFixutre(fixture.Name, f);
// enumerating Test Methods
int t = 1;
foreach (var testMethod in fixture.GetMethods().Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Length > 0))
{
// filtering Actions
var instructions = testMethod.GetInstructions().Where(
i => i.OpCode.Name.Equals("newobj") && ((ConstructorInfo)i.Operand).DeclaringType.IsSubclassOf(typeof(BaseAction))).ToList();
var tempTest = new MintTest(testMethod.Name, t);
// enumerating Actions
for ( int a = 1; a <= instructions.Count; a++ )
{
Instruction action = instructions[a-1];
string actionName = (action.Operand as ConstructorInfo).DeclaringType.Name;
var tempAction = new MintAction(actionName, a);
tempTest.Actions.Add(tempAction);
}
tempFixture.Tests.Add(tempTest);
t++;
}
_fixutres.Add(tempFixture);
f++;
}
}
DLL: Public property Fixtures of the List<MintFixutre> type is created to return just created data model ( List of Fixtures, which contain lists of tests, which contains lists of Actions ). This will be our binding source for TreeView.
public List<MintFixutre> Fixtures
{
get { return _fixtures; }
}
ViewModel of MainWindow (with TreeView inside): Contains object / class from DLL which can parse unit tests DLLs. Also exposes Fixtures public property from the DLL of List<MintFixutre> type. We will bind to it from XAML of MainWindow. Something like that (simplified):
var _exporter = MySuperDllReaderExporterClass ();
// public property of ViewModel for TreeView, which returns property from #4
public List<MintFixture> Fixtures { get { return _exporter.Fixtures; }}
// Initializing exporter class, ParseDllToEntityModel() is called inside getter
// (from step #3). Cool, we have entity model for binding.
_exporter.PathToDll = #"open file dialog can help";
// Notifying all those how are bound to the Fixtures property, there are work for them, TreeView, are u listening?
// will be faced later in this article, anticipating events
OnPropertyChanged("Fixtures");
XAML of MainWindow - Setup data templates: Inside a Grid, which contains TreeView, we create <Grid.Resources> section, which contains a set of templates for our TreeViewItems. HierarchicalDataTemplate (Fixtures and Tests) is used for those who have child items, and DataTemplate is used for "leaf" items (Actions). For each template, we specify which its Content (text, TreeViewItem image, etc.), ItemsSource (in case of this item has children, e.g. for Fixtures it is {Binding Path=Tests}), and ItemTemplate (again, only in case this item has children, here we set linkage between templates - FixtureTemplate uses TestTemplate for its children, TestTemplate uses ActionTemplate for its children, Action template does not use anything, it is a leaf!). IMPORTANT: Don't forget, that in order to "link" "one" template to "another", the "another" template must be defined in XAML above the "one"! (just enumerating my own blunders :) )
XAML - TreeView linkage: We setup TreeView with: linking with data model from ViewModel (remember public property?) and with just prepared templates, which represent content, appearance, data sources and nesting of tree items! One more important note. Don't define your ViewModel as "static" resource inside XAML, like <Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources>. If you do so, then you won't be able to notify it on property changed. Why? Static resource is static resource, it initializes ones, and after that remains immutable. I might be wrong here, but it was one of my blunders. So for TreeView use ItemsSource="{Binding Fixtures}" instead of ItemsSource="{StaticResource myStaticViewModel}"
ViewModel - ViewModelBase - Property Changed: Almost all. Stop! When user opens an application, then initially TreeView is empty of course, as user hasn't opened any DLL yet! We must wait until user opens a DLL, and only then perform binding. It is done via OnPropertyChanged event. To make life easier, all my ViewModels are inherited from ViewModelBase, which right exposes this functionality to all my ViewModel.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
XAML - OnPropertyChanged and commanding. User clicks a button to opens DLL which contains unit tests data. As we using MVVM, then click is handled via commanding. At the end of the OpenDllExecuted handler OnPropertyChanged("Fixtures") is executed, notifying the Tree, that the property, to which it is bind to has been changed, and that now is time to refresh itself. RelayCommand helper class can be taken for example from there). BTW, as I know, there are some helper libraries and toolkits exist Something like that happens in the XAML:
And ViewModel - Commanding
private ICommand _openDllCommand;
//...
public ICommand OpenDllCommand
{
get { return _openDllCommand ?? (_openDllCommand = new RelayCommand(OpenDllExecuted, OpenDllCanExecute)); }
}
//...
// decides, when the <OpenDll> button is enabled or not
private bool OpenDllCanExecute(object obj)
{
return true; // always true for Open DLL button
}
//...
// in fact, handler
private void OpenDllExecuted(object obj)
{
var openDlg = new OpenFileDialog { ... };
_pathToDll = openDlg.FileName;
_exporter.PathToDll = _pathToDll;
// Notifying TreeView via binding that the property <Fixtures> has been changed,
// thereby forcing the tree to refresh itself
OnPropertyChanged("Fixtures");
}
Final UI (but not final for me, a lot of things should be done!). Extended WPF toolkit was used somewhere: http://wpftoolkit.codeplex.com/
Every application contains some settings that are configurable. These settings can more or less put into two categories:
Appearance of application: example can be window location, window size, default options on views etc.
Business rules: these settings will be used by business logic.
In architecture that I implemented, View has its own project (WPF) and ViewModel has its own project (class library). From the lofical standpoint, View should be responsible of loading / saving view related settings, and ViewModel should be responsible for loading / saving business settings.
View settings are easy to handle. Create needed properties in Settings (app.config), and its easy to you can easily load save them.
However, ViewModel cannot access app.config settings through the built-in mechanisms that are Available in View project.
First idea I had was to make some helper methods that will allow me to read / write settings in app.config from ViewModel. What is your opinion? Am I complicating stuff here, or this is acceptable way of handling applic\tion settings?
There are three ways you could go here.
Add a reference to System.Configuration.dll and have your ViewModel project use the ConfigurationManager normally.
Have the ViewModel project ask for the configuration information it needs via constructors or other methods of Dependency Inversion, and have the View project pass it in.
Put the ViewModels and Views in the main application project.
Personally, I would go for option 3 unless there is some reason they need to be in separate assemblies. If they need to be separate, then I would favor option 1 because it's simpler.
Here's a cleaner option, if you would like to keep your assemblies separate and keep your ViewModels testable:
In your ViewModel project, add an interface which provides methods or properties for retrieving and saving business settings. Have your ViewModels accept this interface as a dependency in their constructors.
In your View project, add a class which implements this interface and talks with Settings
eg,
namespace ViewModel
{
public interface IBusinessSettingsStore
{
public string SomeSetting { get; set; }
public int AnotherSetting { get; set; }
}
public class SomeViewModel
{
private IBusinessSettingsStore _businessSettings;
public SomeViewModel(IBusinessSettingsStore businessSettings)
{
_businessSettings = businessSettings;
}
private void DoSomething()
{
Console.WriteLine(_businessSettings.SomeSetting);
_businessSettings.AnotherSetting = 10;
}
}
}
namespace View
{
public class BusinessSettingsStore : IBusinessSettingsStore
{
public string SomeSetting
{
get => Settings.Default.SomeSetting;
set => Settings.Default.SomeSetting = value;
}
public int AnotherSetting
{
get => Settings.Default.AnotherSetting;
set => Settings.Default.AnotherSetting = value;
}
}
}
I've been very impressed with this library: https://www.nuget.org/packages/UserSettingsApplied/. It basically allows you to serialize whatever you want to the user's roaming app config without any effort. It seems well thought out and well tested. This allows the view model to easily persist settings in the app.config.
FYI it is totally OK for the View project to reference View Model. More than that it is pretty much mandatory, so your view can do all of its persistence through the view model layer.
I'm pretty new with Prism and after playing a bit around, there a few questions that arise. I'm trying to create a modular application that basically contains a map control in a shell window. The plugin modules offer different tools for interacting with the map. Some of the modules are pretty independent and simply display pins on the map.
1st question: How would RegionManager come into play for the module-specific classes (presenters) that must interact with the main map control? Usually in a RegionManager you register a specific view which is linked to a ViewModel, but in my case there is one single view (the map view) with multiple presenters acting on it.
2nd question: I need to be able to open several windows (shells) -- a bit like an MS Word document -- that should all be extended by the plugin modules. In a single-shell environment, when the module specific classes were instantiated, they could use the Dependency Injection Container to get a reference to the RegionManager or the Shell itself in order to get access to the map control. However with multiple shells, I don't see how to get access to the map control of the right shell. The dependency container has references to object global to the application, not specific for the shell I'm currently working in. Same is true for the EventAggregator.
Any input would be very welcome,
Ed
After hours of reading Prism-related articles and forums I've come across the article "How to build an outlook style application" on Erwin van der Valk's Blog - How to Build an Outlook Style Application.
In one part of the architecture, a Unity Child Container was used to resolve type instances. That's exactly what I needed for the answer to my 2nd question: I needed to have "scoped" (by window) dependency injection (ex: window scoped EventAggregator, Map control, etc.)
Here's how I create a new window:
private IShellWindow CreateNewShell(IRegionManager regionManager)
{
IUnityContainer childContainer = this.Container.CreateChildContainer();
... register types in child container ...
var window = new ShellWindow();
RegionManager.SetRegionManager(window, regionManager);
window.Content = childContainer.Resolve<MapDocumentView>();
return window;
}
So MapDocumentView and all its components will be injected (if needed) window-scoped instances.
Now that I can have scoped injected objects, I can get the window-scoped map in my module-based MapPresenter. To answer my 1st question, I defined an interface IHostApplication which is implemented by the Bootstrapper which has a MapPresenterRegistry property. This interface is added to the main container.
Upon initialization, the modules will register their presenters and upon the window creation, they will be instantiated.
So for the module initialization:
public void Initialize()
{
...
this.hostApplication.MapPresenterRegistry.Add(typeof(ModuleSpecificMapPresenter));
...
}
The code that initializes the map window:
private void View_Loaded(object sender, RoutedEventArgs e)
{
// Register map in the == scoped container ==
container.RegisterInstance<IMap>(this.View.Map);
// Create map presenters
var hostApplication = this.container.Resolve<IHostApplication>();
foreach (var mapPresenterType in hostApplication.MapPresenterRegistry)
{
var mapPresenter = this.container.Resolve(mapPresenterType) as IMapPresenter;
if (mapPresenter != null)
{
this.mapPresenters.Add(mapPresenter);
}
}
}
The module-specific MapPresenter:
public ModuleSpecificMapPresenter(IEventAggregator eventAggregator, IMap map)
{
this.eventAggregator = eventAggregator;
this.map = map;
this.eventAggregator.GetEvent<AWindowSpecificEvent>().Subscribe(this.WindowSpecificEventFired);
// Do stuff on with the map
}
So those are the big lines of my solution. What I don't really like is that I don't take advantage of region management this way. I pretty much have custom code to do the work.
If you have any further thoughts, I would be happy to hear them out.
Eduard
You have one main view and many child views, and child views can be added by different modules.
I'm not sure that the RegionManager class can be applied in this situation, so I would create a separate global class IPinsCollectionState
which must be registered as singleton in the bootstrapper.
public interface IPin
{
Point Coordinates { get; }
IPinView View { get; }
//You can use a view model or a data template instead of the view interface, but this example is the simplest
}
public interface IPinsCollectionState
{
ObservableCollection<IPin> Pins { get; }
}
Your main view model and different modules can receive this interface as a constructor parameter:
public class MapViewModel
{
public MapViewModel(IPinsCollectionState collectionState)
{
foreach (var item in collectionState.Pins)
{ /* Do something */ };
collectionState.Pins.CollectionChanged += (s, e) => {/* Handle added or removed items in the future */};
}
//...
}
Example of a module view model:
public class Module1ViewModel
{
public Module1ViewModel(IPinsCollectionState collectionState)
{
//somewhere in the code
collectionState.Pins.Add(new Module1Pin());
}
}
The second question can be solved in many different ways:
Application.Current.Windows
A global MainViewModel which contains the list of ShellViewModels and if you add new view model it will be displayed in new window. The bootstrapper is single for all windows.
Some kind of shared state which is passed to the constructor of the bootstrapper.
I don't know how these windows are related between themselves, and I don't know which way is the best, maybe it is possible to write an application with separated windows.