In MEFedMVVM viewmodels are instantiated using ViewModelLocator. MEFedMVVM is really powerful, since you can have an arbitrary constructor for your viewmodels:
[ExportViewModel("MyViewModel")]
public class MyViewModel : NotifyPropertyChangedBase
{
[ImportingConstructor]
public MyViewModel(IMediator mediator, IContainerStatus containerStatus, IDispatcherService dispatcherService)
{
}
}
IMediator, IContainerStatus and IDispatcherService are service interfaces which are instantiated through MEF. Obviously, I can create my own services if needed.
The problem
When my view has loaded it needs to assign a member of one of its children's readonly property with data from the viewmodel. Ideally, I would bind this variable directly in XAML, but I cannot do that since the property is readonly and its member is not an attachable property. Currently, I have an ugly workaround:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var viewModel = DataContext as MyViewModel;
this.child.SomeReadonlyProperty.Data = viewModel.MyData;
}
I want to get rid of this coupling of the view and the viewmodel. Both MEFedMVVM and Prism provide different patterns that might be helpful, but I have no idea which to use - and how. Is it ok to let services have access to both the view and the viewmodel?
Q: What pattern should I use to remove the coupling?
I think I would be inclined to create an interface which your view can implement that will describe the behavior you need to set the property:
interface IMyView
{
void SetReadOnlyProperty(object value);
}
You can then create your implementation of SetReadOnlyProperty in the view, export the view as type IMyView and finally, create an import on your viewmodel of type IMyView which will expose your method and allow you to perform calls from the viewmodel. I believe this doesn't go against the ethos of MVVM because the viewmodel isn't aware of the view, it just has an interface that will expose what you need.
Related
I need to connect methods in View (WPF window) to events in ViewModel. Is it violation of MVVM pattern to DirectCast Object DataContext in view to concrete VM type and connect its events? If yes, is there better way to do it?
First look at what the methods in the view do. If they manipulate the view, consider adding properties to the viewmodel that you change in the events in the viewmodel and bind the view to. This way, by binding the view to properties you eliminate the need for code in the view.
If the methods contain other logic consider moving that logic to the viewmodel.
In other cases casting a DataContext to a viewmodel or interface can be a valid option and is not a violation of the MVVM pattern.
When adding code to a view, do consider testing. Automated/unit testing a view is harder than testing a viewmodel.
It is not a violation of the MVVM pattern, but the more abstract the better, of course (not because of MVVM but as a general good practice).
If you're setting your DataContext on XAML, you may be able to keep it abstract by using Interactivity EventTrigger and CallMethodAction... Maybe. But if you're setting it on code-behind (via injection or whatever), you're left with either casting the DataContext to a known type, or using Reflection (I wouldn't >_>).
Generally, creating an interface for your ViewModel, so you keep a decent level of abstraction and only expose what the view needs to know instead of its whole implementation, is good enough for most scenarios.
public interface IMyViewModel
{
event EventHandler MyEvent;
}
public class MyViewModel : IMyViewModel
{
public event EventHandler MyEvent;
// More viewmodel related stuff
protected virtual void OnMyEvent(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
public class MyWindow : Window
{
public MyWindow(IMyViewModel viewModel)
{
this.DataContext = viewModel;
InitializeComponent();
(this.DataContext as IViewModel).MyEvent += MyEventHandler;
}
private void MyEventHandler(object sender, EventArgs e)
{
// Do view related stuff
}
}
Using MVVM pattern and WPF, I would like to bind my controls to two properties. Let's say I have a label that is bind to property on VM1 and I would like to bind it as well to the property on VM2 to send received value from VM1 to VM2.
I could use messenger and for multiple items Tuple Class, but I was wondering if there is another solution for that. Multibinding? but then I need ValueConverter.
Thank you for any suggestions.
Since The View-Model is an abstraction of the view that exposes public properties and commands, it doesn't make a lot of sense for a view to have two view-models the way you explained. It'll be more rational if there is a VM class as the view-model of your view that has two properties of type VM1 and VM2. Then the binding would be on VM.VM1.YourText and you can notify VM2 via events like this:
in VM1:
public event EventHandler<EventArgs> ValueChanged;
string _yourText;
public string YourText
{
get
{
return _yourText;
}
set
{
_yourText= value;
if (ValueChanged != null)
ValueChanged(_yourText, new EventArgs());
}
}
In VM:
public VM1 Vm1 {get; set;}
public VM2 Vm2 {get; set;}
public VM()
{
InitializeComponent();
Vm1 = new VM1();
Vm2 = new VM2();
Vm1.ValueChanged += Item_ValueChanged;
DataContext = this;
}
void Item_ValueChanged(object sender, EventArgs e)
{
VM2.YourAnotherText = sender.ToString();
}
If 2 properties are connected, usually INotifyPropertyChanged can be utilized to notify about a change of 2 or more properties if on the same ViewModel.
What I understand is that you want also to notify a View attached to a ViewModel about a change of a property on another ViewModel. This is usually done by letting ViewModels exchange information.
If that's a one rare case, using message bus for that might be an overkill. Usually keeping a reference to each view model and changing properties from outside should be all right. To keep separation of concerns you can create an interface on one or both viewmodels and reference this interface instead of a concrete type.
Overall keeping a single binding between a control and property keeps it simple and easy to understand and you should worry about making sure that this property handles all changes to/from other VMs.
Using MVVMLight toolkit in a WPF app.
I have a ViewModel class that encapsulates the application's settings, and a corresponding View to allow the user to change them. The settings are stored in the registry.
I'm adding some status indicators to the main View to reflect whether some settings are toggled on or off. It makes sense to bind the visibility of these to properties in the MainViewModel.
Is it possible to bind properties in the MainViewModel to properties in the SettingsViewModel, or should I use messages to tell the MainViewModel that something has changed in the SettingsViewModel?
Thanks for any suggestions...
A simple way to do it by exposing the SettingsViewModel as a property on your MainViewModel like this:
Public SettingsViewModel Settings
{
get
{
return settingsViewModel;
}
}
Then in your MainView.xaml your binding can do this:
"{Binding Path=Settings.ASettingOnTheSettingsVM}"
If your SettingsViewModel is launched from the MainViewModel and you have implemented the INotifyPropertyChanged interface then you can just listen to the SettingsViewModel.PropertyChanged event:
In MainViewModel:
SettingsViewModel settingsViewModel = new SettingsViewModel();
settingsViewModel.PropertyChanged = SettingsViewModel_PropertyChanged;
...
private void SettingsViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Monitor property changes here
}
recently I started working with Prism in Silverlight. I want to use the EventAggregator to Subscribe and Publish events between two ViewModels. As I saw on some guides, the ViewModel's ctor should accept IEventAggregator as a parameter. I can't find out how to do this hence my View always wants to initialize the ViewModel with a parameterless ctor.
My ViewModel ctor:
MyViewModel(IEventAggregator eventAggregator)
{
// get the event....
}
My View:
<UserControl ....>
<UserControl.Resources>
<ViewModels:MyViewModel x:Key="MyViewModel"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MyViewModel}}">
....
<Grid/>
</UserControl>
I can instantiate the ViewModel in the ctor of the View, and then assign it to its DataContext, but then I must have an IEventAggregator in my View, which I also cannot get. but this is probably not the correct way to pass an IEventAggregator (or any other object! - IUnityContainer for example) to the ViewModel.
Can someone tell me what I'm doing wrong?
You have to resolve your dependency via unity. Have a look at the prism MVVM examples and the ui composition. There the view does not create the view model, but it is exactly the other way round. The view model gets the view injected via constructor injection. The view model sets itself as view model for the view:
public interface IView
{
IViewModel ViewModel{get;set;}
}
public interface IViewModel { }
public View:UserControl, IView
{
public IViewModel ViewModel
{
get{return DataContext as IViewModel;}
set{DataContext = value;}
}
}
public ViewModel:IViewModel
{
public ViewModel(IView view, IEventAggregator eventAggregator)
{
view.ViewModel = this;
//get the event...
}
}
Using this approach you have to register the view model and the view to unity. Afterwards you only have to resolve the view model, the view is injected by the container.
To get the view to the right place on the user interface you have to register the view to a region using the RegionManager. When this is all set up, creating a new view model instance results in adding the view into the registered region so that it shows up on the user interface.
Other than having the ViewModel hook itself into the data context of the view (which I don't like at all), there are two other options that I can think of in Silverlight.
Utilize the ServiceLocator pattern to allow your static resources to create themselves via the container. MVVMLight has a fairly good pattern for this.
Use a framework like Caliburn.Micro, which plugs in a nice set of conventions that will wire up many things based on naming conventions, including bindings and viewmodels.
Maybe you've solved it already but
http://www.emileinarsson.se/silverlight-4-mvvm-prism-unity-dependency-injection/
this post explains how to use Unity in a MVVM environment.
I am new in the Wpf & Mvvm world , but I have found a couple of examples and just found that there is some different way to instantiate the model. I would like to know the best/correct way to do it. both ways are using Unity
What I've foud:
var navigatorView = new MainView();
navigatorView.DataContext = m_Container.Resolve<INavigatorViewModel>();
m_RegionManager.Regions["NavigatorRegion"].Add(navigatorView);
What I did:
var navigatorView = m_Container.Resolve<MainView>;
m_RegionManager.Regions["NavigatorRegion"].Add(navigatorView);
and I changed the constructor to receive viewmodel so I can point the datacontext to it:
public MainView(NavigatorViewModel navigatorViewModel)
{
this.DataContext = navigatorViewModel;
}
Other examples I've found another way like:
...vm = new viewmodel
...m = new model
v.model = vm;
get/set DataContext
cheers
I like Igor's suggestion, but without the viewmodel having knowledge of the view. I prefer my dependencies to go one direction (View -> ViewModel -> Model).
What I do is ViewModel-First and just DataTemplate the viewmodel. So I do this:
MainViewModel mainViewModel = container.Resolve<MainViewModel>();
region.Add(mainViewModel, "MainView");
region.Activate(mainViewModel);
With the addition of the ViewModel -> View mapping done with a WPF datatemplate (I don't think this approach is possible with Silverlight, though)
App.xaml:
<Application.Resources>
<DataTemplate DataType="{x:Type viewModels:MainViewModel}">
<views:MainView />
</DataTemplate>
</Application.Resources>
That's it! I love this approach. I like the way it feels like magic. It also has the following advantages:
Don't have to modify constructors to suit the mapping
Don't have to register type for IMyViewModel in the container... you can work with concrete types. I like to keep my registrations to application services like IViewRegistry or ILogger... those kinds of things
You can change the mapping using resources scoped to a particular view that a region is in (this is nice if you want to reuse your ViewModels but want them to look different in different areas of the application
What you got there makes sense and in both cases is a View-first approach to creating a viewmodel. I.e. the view creates the ViewModel. In the original example the viewmodel is created outside of the view (and is sometimes referred to as marriage pattern), but as far as I am concerned that's the same thing - creation of the view creates the ViewModel.
If this suits your needs stick with it. Another approach you might look into is ViewModel first where the viewmodel takes a dependency on the view like so:
//In the bare-bones(i.e. no WPF dependencies) common interface assembly
interfac IView {
void ApplyViewModel(object viewmodel);
}
interface IMainView : IView {
//this interface can actually be empty.
//It's only used to map to implementation.
}
//In the ViewModel assembly
class MainViewModel {
public MainViewModel(IMainView view) {
view.ApplyViewModel(this);
}
}
public partial class MainView : UserControl, IMainView {
void ApplyViewModel(object viewmodel){
DataContext = viewmodel;
}
}
Then you can inject this view like so:
IRegion region = regionManager.Regions["MainRegion"];
//This might look strange as we are resolving the class to itself, not an interface to the class
//This is OK, we want to take advantage of the DI container
//to resolve the viewmodel's dependencies for us,
//not just to resolve an interface to the class.
MainViewModel mainViewModel = container.Resolve<MainViewModel>();
region.Add(mainViewModel.View, "MainView");
region.Activate(ordersView.View);