How do the View and the Model interact with the sub ViewModels? - c#

I have an MVVM based WPF application with the sub ViewModels interacting with each other and the main ViewModel via a mediator class (mediator design pattern). Does the main ViewModel somehow expose the properties of the sub ViewModels to the View and the Model? One way would be to make the main ViewModel still implement the interfaces of sub ViewModels and replace the functionality of the interface functions with calls to the mediator but then these callbacks have no return type and the View and Model may require some data from subViewModels. For example,
MainWindow wants subViewModel1.state but knows only the mainViewModel. In my current implementation the mainViewModel can only set the subViewModel1.state and subViewModel1 can send it to the mainViewModel but this would an async callback.

Related

Prism: ViewModelLocator creates the view model instance(s) but how can I target it?

Is there a way whereby I can identify a ChildViewModel instance, which was created by prism's ViewModelLocator, upon opening it's corresponding window?
I would like to trigger that the ChildViewModel should load it's data, based on parameters originating from the MasterViewModel.
In code, in MasterViewModel has an ICommand in a which is responsible for requesting opening a new child window by publishing an event, and there is a corresponding subscriber.
public ICommand OpenNewChildWindow()
{
Publish(new OpenNewChildWindowPubSubEvent());
// Maybe I can publish a new PubSubEvent here
// but how can I target just the recently created ChildViewModel?
}
Notice that the MasterViewModel knows nothing about the UI implementation.
The the subscriber calls ShowWindow method on a custom WindowManager which basically resolves the View (Window in this instance) which corresponds to the ViewModel which was passed in.
public void ShowWindow(Type viewModelType)
{
Type view = ResolveView(viewModelType);
Window w = (Window)Activator.CreateInstance(view);
w.Show();
}
The xaml for the window the appropiate
ViewModelLocator.AutoWireViewModel="True"
Go for a view model-first style of navigation. If you pass a (child-)view model instance (instead of a type) to ShowWindow, you can create that with whatever data you need.
Probably, you pass the data around as payload of the OpenNewChildWindowPubSubEvent, and the subscriber then creates the view model. Or you create the view model immediately in the command and pass that on as payload of the event.
Anyway, don't resolve the view type from the view model type just to resolve the view model type from the view :-)
BTW, the ViewModelLocator is great and really simplifies things, but you don't want to use it here, because you're not navigating within one shell, but creating new windows. If you would, your view model would implement INavigationAware and you'd pass the data to the child view model as parameter to RequestNavigate...

WPF - MVVM view model first

I am building a large scale WPF app - kind of a desktop that hosts multiple modules such as multiple Terminal windows over RS232/Ethernet, Register analyzer, automation tools, etc.
I am working with MVVM architecture where my view (XAML) instantiate the corespondent viewmodel in its resource section. and the view model is set in the data-context of the view.
in this method the view created first.
however, I red about another method called VM first, meaning view model is instantiated before the view, and I understood the theory that stands behind it. What I didn't understand is when and who instantiate the view and how it happens without coupling to the view.
I'll be more than happy to hear your thoughts, and please if someone can supply code samples it would be great..
Thanks in advance.
I'm using MVVM heavily in my projects and can share my view on this.
In my projects the view never instantiates any VM. Usually I have some kind of manager which takes care that the corresponding VM is created.
This I'm assign to the datacontext of some top-level UI control (Window for instance). The view is always defined by a style where the target type is set to the type of the view model.
The startup code just creates a Window and the main viewmodel. The VM is assigned and the rest is done by the WPF (.net) runtime so to say.
So I have a large style file, where all the styles for each viewmodel defines the corresponding view (usually a usercontrol).
This is the way I'm doing things, there are for sure others too.
hth
In my WPF / MVVM applications I use ViewModels with two constructors - one for design time (no paramaters - mock version of required components are set directly) and another for runtime (required components are injected as parameters via IoC). This allows for (mock) data to be displayed inside the Visual Studio designer for UI testing purposes.
So the simple case looks like ...
public class MainViewModel : ViewModelBase
{
private IDataFactory _DataFactory;
public MainViewModel()
{
_DataFactory = new DesignTimeMockDataFactory();
LoadData();
}
[PreferredConstructor]
public MainViewModel(IDataFactory dataFactory)
{ _DataFactory = dataFactory; }
public void LoadData()
{ DataItems.AddRange(_DataFactory.GetDataItems()); }
public ExtendedObservableCollection<DataItem> DataItems { get; private set; }
}
The design time usage can be set directly in the XAML ...
<Window x:Class="MainView"
d:DataContext="{d:DesignInstance Type=MainViewModel, IsDesignTimeCreatable=True}"
...
The run-time ViewModel is set in the code behind of the View ...
public MainView()
{
InitializeComponent();
var viewModel = SimpleIoc.Default.GetInstance<MainViewModel>();
DataContext = viewModel;
Loaded += (s, e) => viewModel.LoadData();
}
The View's Loaded event is set to call the LoadData() method of the ViewModel to trigger data loading, once the View is displayed. If LoadData() is slow, it can be changed into an async method to prevent the UI from blocking.
For those of you complaining that this is a too tightly coupled construct, my view is that is exactly how they are supposed to be. Although the View and ViewModel are separate entities, each View knows exactly what type of ViewModel it requires, and that's unlikely to change over the project development life-cycle. Using a Locator type class to hide the ViewModel constructor calls is an unnecessary level of abstraction in my opinion.
To decouple the view from the view-model, something else needs to instantiate the view model and manage its lifetime and sharing. That job might fall to an IoC container, or simple, manual dependency injection. It's entirely up to you.
E.g. from Paul Stovell's article:
public CalculatorView(CalculatorViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
It all depends on you're trying to achieve by decoupling. One reason might be so that you can have multiple views over the same view-model - in that case, whatever creates the views needs to also create the view-model.
Another may be to swap the view-model of an existing view out with another view-model without destroying the view. In that case, maybe you already have two existing view-models and you assign them to the view's DataContext as required.
view.DataContext = viewModels[0];
view.DataContext = viewModels[1];
when your application grows you usually face these decisions. Usually you have "always" both elements together the View and the ViewModel it's not about what comes first it's more like what will you use to instantiate the two elements (view and viewmodel).
For larger projects, when I had the need, I used a class called ViewModelResolver. It obviously has an interface IViewModelResolverso it can be injected nicely.
It can either return a ViewModel based on convention based on type or a string representation and uses reflection to instantiated it.
You can also pass in a ViewModel (or type) and get the matching view with the passed in view model as DataContext (view ViewModel marriage) or you can define other custom scenarios that you need for instantiating either view or ViewModel.
hope that helps
So the main point is to have an intermediate class that acts like some sort of factory service that takes car of bringing views and view models together and instantiate them.
This gives you more freedom and a good place to separate out those decisions from the ViewModel directly.

What is the proper way to embed tabs in TabControl

I'd like to statically embed two tabs in my application and be informed when the tabs change but I don't know how to do it maintaining all the MVVM rules.
I have MainViewModel which inherits from Conductor<IScreen>.Collection.OneActive
then I've got my sub viewmodels FirstViewModel and SecondViewModel which inherits from Screen.
Now my MainView looks simply like this:
<TabControl x:Name="Items" />
and I attach my views in its vm constructor:
Items.AddRange(
new IScreen[]
{
new FirstViewModel(events),
new SecondViewModel(events)
});
But this way I need to pass events (EventAggregator) in params.
I'd like to be able to use [ImportingConstructor] to do it automatically.
Also it doesn't look right for me to create those viewmodels in code. I think I should specify those tabs directly in MainView but when I do it like this I can't take adventage of Conductor.Collection.OneActive and Screen's events like OnActivate and OnDeactivate.
What is the proper way to do this?
Assuming your event aggregator is a singleton, then you leave it up to MEF to resolve your dependencies by injecting the FirstViewModel and SecondViewModel into the MainViewModel via constructor injection.
As these both have a dependency on IEventAggregator, that will also be resolved as part of the construction of MainViewModel.
If you have a specific instance of the event aggregator that you need to inject, or you wish to control the instantiation of the child view models from the MainViewModel, then you should introduce a view model factory which is injected into your MainViewModel.

Access MVVM parent view model from within collection

The picture above illustrates my program. Arrows indicate Binding.
My MainWindow.xaml has its datacontext set as MainVM.
The Window has a tab control binded to a ObservableCollection of ViewModels. Using a data template, the tab control displays views corresponding to the ViewModel.
The ObservableCollection is found in MainVM.
How do I access properties found in MainVM from ViewModel (enclosed in the ObservableCollection)?
I am open to answers that are require modification of my programming model.
Building on what Kent said . . .
From an architectural standpoint, your ViewModel class shouldn't need to know anything about MainVM. Anything that it needs to know should be passed in to the instance of ViewModel so that it can stay atomic, and not have to worry about things that call it. From a SOLID standpoint, this is the Dependency Inversion principle.
Whatever properties from MainVM need to be accessed within ViewModel should be passed as part of the construction of the instance of ViewModel. Personally, I'm a fan of including them in a constructor, but you could also expose them as properties.
An overkill method (which I don't recommend), is setting up your constructor to take an instance of MainVM, and passing in this in the constructor. e.g.:
public class ViewModel
{
private readonly MainVM _parent;
public MainVM Parent => _parent;
public ViewModel(MainVM parent)
{
_parent = parent;
}
}
then when you create your ViewModel you can just:
ViewModel vm = new ViewModel(this);
Depending on how much interaction you need, you could kind of go the other way by firing events from the ViewModel class and having MainVM handle them and set properties on the individual ViewModel accordingly.
Another option would be when you instantiate the ViewModel Collection (which I assume is done in MainVM somewhere?), you could just pass in MainVM as a constructor parameter and keep and instance of MainVM inside of each ViewModel.
Who creates instances of ViewModel? If there is a tight relationship between MainVM and ViewModel then perhaps ViewModel should take an instance of MainVM in its constructor.
The alternative is to come up with a different design that does not require ViewModel to have access to the MainVM. Doing so would require more information (and a separate question).

Having trouble deciding how to wire up a UserControl with MVVM

I've been doing the best I can to try to stay true to the separation recommended by the MVVM pattern. One thing I haven't figure out how to do correctly has to do with initializing my UserControls.
My most recent example of this has to do with a library that I wrote to talk to some low-level hardware. That assembly happens to have a UserControl that I can simply drop into any GUI that uses this hardware. All that is necessary for it to work is to set a reference to the object that has access to the low level methods.
However, that's where my problem lies -- currently, the UserControl is added to the GUI via XAML, where I define the namespace and then add the UserControl to my window. Of course, I have no control over its creation at this point, so the default constructor gets called. The only way to set the necessary reference for hardware control involves calling a method in the UC to do so. The ViewModel could feasibly call a method in the Model, e.g. GetController(), and then call the method in the UserControl to set the reference accordingly. The GUI can pass a reference to the UserControl to the ViewModel when said GUI creates the ViewModel, but this violates MVVM because the ViewModel shouldn't know anything about this control.
Another way I could deal with this is to not create the UserControl in XAML, but instead do it all from code-behind. After the ViewModel gets initialized and retrieves an initialized UserControl (i.e. one that has the low-level object reference set), it can set the Content of my Window to the UserControl. However, this also violates MVVM -- is there a way to databind the Content of a Window, TabControl, or any other element to a UserControl?
I'd like to hear if anyone has had to deal with this before, and if they approached it the first or second way I have outlined here, or if they took a completely different approach. If what I have asked here is unclear, please let me know and I'll do my best to update it with more information, diagrams, etc.
UPDATE
Thanks for the responses, guys, but I must not have explained the problem very well. I already use RelayCommands within the UserControl's ViewModel to handle all of the calls to the hardware layer (Model) when the user clicks in the control in the UserControl itself. My problem is related to initially passing a reference to the UserControl so it can talk to the hardware layer.
If I create the UserControl directly in XAML, then I can't pass it this reference via a constructor because I can only use the default constructor. The solution I have in place right now does not look MVVM-compliant -- I had to name the UserControl in XAML, and then in the code-behind (i.e. for the View), I have to call a method that I had added to be able to set this reference. For example, I have a GUI UserControl that contains the diagnostics UserControl for my hardware:
partial class GUI : UserControl
{
private MainViewModel ViewModel { get; set; }
public GUI( Model.MainModel model)
{
InitializeComponent();
ViewModel = new MainViewModel( model, this.Dispatcher);
ViewModel.Initialize();
this.DataContext = ViewModel;
diagnostics_toolbar.SetViewModel( ViewModel);
user_control_in_xaml.SetHardwareConnection( model.Connection);
}
}
where the outer class is the main GUI UserControl, and user_control_in_xaml is the UserControl I had to name in the GUI's XAML.
Looking at this again, I realize that it's probably okay to go with the naming approach because it's all used within the View itself. I'm not sure about passing the model information to user_control_in_xaml, because this means that a designer would have to know to call this method if he is to redo the GUI -- I thought the idea was to hide model details from the View layer, but I'm not sure how else to do this.
You will also notice that the main GUI is passed the Model in the constructor, which I assume is equally bad. Perhaps I need to revisit the design to see if it's possible to have the ViewModel create the Model, which is what I usually do, but in this case I can't remember why I had to create it outside of the GUI.
Am new to MVVM myself but here's a possible solution:
Create a property in your VM that is of the object type (that controls the hardware) and bind it to an attached property on your UserControl. Then you could set the property in your VM using dependency injection, so it would be set when the VM is created. The way I see it, the class that talks to the hardware (hardware controller) is a service. The service can be injected to your view model and bound to your UserControl. Am not sure if this is the best way to do it and if it is strict enough to all the MVVM principles but it seems like a possible solution.
if your question is: How do i show my viewmodel in the view? then my solution is always using viewmodelfirst approach and datatemplates.
so all you have to do is wire up your viewmodel via binding to a contentcontrol.content in xaml. wpf + datatemplates will do the work and instantiate your usercontrol for your viewmodel.
You are right, the ViewModel shouldn't know about anything in the View - or even that there is such a thing as a View, hence why MVVM rocks for unit testing too as the VM couldn't care less if it is exposing itself to a View or a test framework.
As far as I can see you might have to refactor things a little if you can. To stick to the MVVM pattern you could expose an ICommand, the ICommand calls an internal VM method that goes and gets the data (or whatever) from the Model, this method then updates an ObservableCollection property of the data objects for the View to bind to. So for example, in your VM you could have
private ICommand _getDataCommand;
public ICommand GetDataCommand
{
get
{
if (this._getDataCommand == null)
{
this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
}
return this._getDataCommand;
}
}
private void GetMyData{
//go and get data from Model and add to the MyControls collection
}
private ObservableCollection<MyUserControls> _uc;
public ObservableCollection<MyUserControls> MyControls
{
get
{
if (this._uc == null)
{
this._uc = new ObservableCollection<MyUserControls>();
}
return this._uc;
}
}
For the RelayCommand check out Josh Smiths MSDN article.
In the View you could either call the ICommand in the static constructor of your UC - I am guessing youwould need to add an event in your class for this - or call the ICommand from some sort of click event on your UC - maybe just have a 'load' button on the WPF window. And set the databinding of your UC to be the exposed observable collection of the VM.
If you can't change your UC at all then you could derive a new class from it and override certain behaviour.
Hope that helps a bit at least, like I say, have a look at Josh Smiths MVVM article as he covers the binding and ICommand stuff in there brilliantly.
If you set the DataContext of the Window or UserControl containing thisUserControl to the main view model, the user control can call SetHardwareConnection() on itself in its Loaded event (or DataContextChanged event handler).
If that's not possible because you're saying the UserControl is 'fixed', you should derive from it or wrap it up in another UserControl, which would serve as a MVVM 'adapter'.
(In order to bind the window: you could make the MainViewModel a singleton with a static Instance property and use DataContext="{x:Static MyClass.Instance}". A nice way to get things going quickly)
Note; this is based on my understanding that MVVM works because of Bindings.. I always bind the control to a ViewModel, not pass a ViewModel as a parameter.
Hope that helps!

Categories

Resources