I faced with some problems when designing the architecture of my extensible program.
I'm using MEF, MMVM Light Toolkit, and AvalonDock.
The first problem is how to display a view for some ViewModel
imported from another assembly using MEF.
To solve it, I'm exporting ResourceDictionary
where I'm defining DataTemplate
's for views declared in this assembly.
Dictionary
:
<ResourceDictionary
...>
<DataTemplate DataType="viewmodels:MyViewModel">
<views:MyViewForViewModel/>
</DataTemplate>
</ResourceDictionary>
And in constructor of MainWindow
I'm importing all ResourceDictionaries
and merging them with MainWidow.ResourceDictionary
.
Is it good? It's also possible to specify 'scope' of ResourceDictionary
to import it not to MainWindow
, but to Application
for example.
The second problem is ICommands
and CommandBindings
.
To populate Menu
I'm exporting 'MenuItems' where I'm defining ICommand
, Text
, and other stuff, but I don't know how to export CommandBinding
, should I use RelayCommand
for cases when I can't create CommandBinding
?
The third problem is dialogs.
I found great article Showing Dialogs When Using the MVVM Pattern and easily adapt it to MEF. But, for example, I have an IDatabaseService
which don't have any View.
The Workspace
, main ViewModel, storing an instance of IDatabaseService
and creating menu item: Connect to Database
. Using IDialogService Workspace opening some imported IConnectToDbDialog
so Workspace
doesn't know anything about it. When dialog closed, the SqlConnectionString
should be passed to IDatabaseService
.
So who must pass this SqlConnectionString
, IConnectToDbDialog
, or Workspace
.
The fourth problem is how to communicate with IDatabaseService
correctly.
For example. In some View I have Button
: 'Create Item In Database'. And how should I call IDatabaseService
method CreateItem(ElementType elementType)
when button clicked?
The problem is that there are a lot of buttons which create Items
with different ElementType
in the database, so, I think, it's right to create some ICommand
with parameters and create only one handler for this command which will invoke some method in IDatabaseService
. But I don't know how.
The other solution is to send messages to IDatabaseService
from ViewModel
to create the item.
Which way is better?
Try to answer your questions.
It is good. You can merge either on XAML or code behind but I prefer XAML. You can put it on MainWindow.Xaml, which is in scope of main window or on App.Xaml, which is in application scope.
I did not export views before. In my opinion, if you put CommandBindings under Menu, it does not matter when it is exported then imported if the event handler in the scope of imported environment.
It depends. Theoretically you can put the service call in either owner's view model or dialog's view model. If your dialog have a create/submit button, for instance, and you expect the dialog keeps alive until submission is successful, then put it in dialog's view model so that you can keep it open when you handle exceptions. if you do not need the dialog keeps open, then you can put the logic in owner's view model after dialog is closed.
Command is better. Considering the view model gets IDatabaseService object from IoC container, You might have one ICommand property that accepts ElementType parameter or a paramerter can map to ElementType. In the execute method you call CreateItem passing the parameter directly or from mapper. On you XAML, you put type in the command binding. Does it make sense?
Hope it can help.