Stuggling with WPF MVVM Unity, Generic Repository - wpf

How can I inject my CustomerRepository into my CustomerVM view model? In my view, a WPF Page I have:
<Page.DataContext>
<viewModel:CustomerVM/>
</Page.DataContext>
But my view model constructor obviously has parameter passed in,
public CustomerVM(ICustomerRepository customerRepository)
{
//this._customerRepository = customerRepository;
}
I get
Type 'CustomerVM' is not usable as an object element because it is not
public or does not define a public parameterless constructor or a type
converter.
Really struggling to heck.
Any help appreciated.

I don't think you can initialize the DataContext within the XAML if you're using dependency injection. Set the DataContext in the code-behind for the view so Unity can resolve the dependencies. Try adding this to YourView.xaml.cs:
public YourView(CustomerVM viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
The above will work if you are resolving your views through Unity. If not, you can also use the ServiceLocator to resolve the view model:
using Microsoft.Practices.ServiceLocation;
public YourView()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<CustomerVM>();
}
You might also need to add the following somewhere in your registration code to setup the ServiceLocator if you're not using Prism:
ServiceLocator.SetLocatorProvider(new ServiceLocatorProvider(() => new UnityServiceLocator(_unityContainer)));

Related

WPF, MVVM IoC: Alternative to Service Locator Pattern. Need dependency in View code behind

Following several guides I have a application layout like below using WPF .NET 4.7.1 and MVVM-Light. I'm totally new to WPF btw.
App.xaml:
<Application x:Class="My.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodel="clr-namespace:My.ViewModel"
StartupUri="View\MainView.xaml">
<Application.Resources>
<ResourceDictionary>
<viewmodel:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
</Application.Resources>
That registers the "ViewModelLocator" class as a resources and sets the WPF startup to "View/MainView.xaml".
MainView.xaml:
<Window x:Class="My.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
Where the ViewModelLocator is used like a Service Locator Pattern. Here setting the DataContext to my "MainViewModel" (not shown). As much as I do not like this, I can live with it in the WPF XAML context. However now it turns out that I need a dependency in the code-behind of the view (not the ViewModel).
MainView.cs:
public partial class MainView : INotifyPropertyChanged
{
public MainView()
{
// Need to access dependency here.
}
}
Now I could just call the ViewModelLocator directly in that constructor and have that resolve from my IoC container - but then I've completely given in and accepting that pattern.
I would prefer to have the dependency injected in the ctor of course, and if that is possible, I would also leave the ViewModelLocator entirely and inject the ViewModel here.
So question is, are there some standard way of instructing WPF application to use my container? And if yes, is it adviceable to go down that path and not use the ViewModelLocator thing?
You absolutely do not have to use the ViewModelLocator (Side note, the service locator pattern has had it's fair share of criticism lately as an anti-pattern, but I'll let you form your own opinion). MVVM Light and other Libraries basically give you access to a tool kit. You don't need to use all of the tools, and you should only use what is necessary for your specific domain.
Outside of the ViewModelLocator, there are two patterns known as ViewModel First and View First both have their pro's and cons. However both provide a means to decouple your code, which means it's not difficult to switch later.
As for constructing an application using MVVM Light without the service locator, my implementation of the View First method looks something like this.
I've heard the opinion that ViewModel First is preferred, however I find View First to be more simplistic for Test Driven Development (TDD)
App.xaml.cs (Application Code Behind)
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bootStrapper = new BootStrapper();
//Container Builder
var container = bootStrapper.BootStrap();
var mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
}
}
BootStrapper.cs (I'm using AutoFac in this case, but you can easily substitute.)
public class BootStrapper
{
public IContainer BootStrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<MainWindow>().AsSelf();
builder.RegisterType<MainWindowViewModel>().AsSelf();
return builder.Build();
}
}
MainWindowViewModel.cs
//I rolled my own ViewModelBase, but you can use MVVM Light's ViewModelBase
public class MainWindowViewModel : ViewModelBase
{
public string DisplayProgram
{
get { return _displayProgram; }
//MVVM Light's ViewModelBase uses RaisePropertyChanged();
set { _displayProgram = value; OnPropertyChanged(); }
}
public void Initialize()
{
//Called from view code behind.
}
}
MainWindow.xaml.cs (MainWindow Code Behind)
//When MainWindow.Show()..
public partial class MainWindow : Window
{
private readonly MainWindowViewModel _viewModel;
//Container resolves dependencies
public MainWindow(MainWindowViewModel viewModel)
{
//Let base Window class do its thing.
InitializeComponent();
//Handle loaded event
Loaded += MainWindowLoaded;
//Hold on to the MainWindowViewModel, and set it as the windows DataContext
_viewModel = viewModel;
DataContext = _viewModel;
}
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
_viewModel.Initialize();
}
}

WPF MVVM share object between view model and code behind

I am trying to share the following object between my view model and code behind
Dictionary<ItemTypeType, Dictionary<string, Dictionary<int,List<ConfigParameter>>>> ItemToConfigParametersValues
This object is not used in the XAML. It is used in the code behind for several dynamically generated UI Elements.
What is the MVVM Light way of doing this?
Well, the ViewModel is typically set as the "DataContext" of the View. Code-behind is part of the View.
So... just expose the data from your ViewModel. In your code-behind, you can access it using the DataContext property (with appropriate casting).
I would store the viewmodel in a variable so I didnt need to cast DataContext everytime... In the MainWindow.xaml.cs code behind for example:
private MainWindowViewModel _vm;
public MainWindow()
{
InitializeComponent();
this._vm = new MainWindowViewModel();
// this._vm.MyProperty = ... (or does the vm instantiate MyProperty?)
this.DataContext = this._vm;
}
private void HandleSomeEvent(object sender, RoutedEventArgs e)
{
var sharedObject = _vm.MyProperty;
}

Prism5 PopupWindowAction and injection

I looked at part of InteractivityQuickstart official example.
<prism:InteractionRequestTrigger SourceObject="{Binding ItemSelectionRequest, Mode=OneWay}">
<prism:PopupWindowAction>
<prism:PopupWindowAction.WindowContent>
<views:ItemSelectionView />
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
So, ItemSelectionRequest called less-parametre constructor
public ItemSelectionView()
{
this.DataContext = new ItemSelectionViewModel();
InitializeComponent();
}
in code-behind of ItemSelectionView.
Questions:
1) How possible to set DataContext without "new", because
public ItemSelectionView(ItemSelectionViewModel model)
or
[Dependency]
public ItemSelectionViewModel ViewModel
{
set { this.DataContext = value; }
}
doesn`t work.
I need to get some services in ViewModel => i need to call something like this
public ItemSelectionViewModel(IEventAggregator eventAggregator)
{
_eventAggregator=eventAggregator;
}
If you need a service for your Popup ViewModel, you could get it by using the ServiceLocator.
public ItemSelectionView()
{
InitializeComponent();
DataContext = ServiceLocator.Current.GetInstance<ItemSelectionViewModel>();
}
Rather than use the ServiceLocator to set your ViewModel as Brian Lagunas suggests, why not have a parameterless constructor for ViewModel, set the ViewModel directly in your View class (XAML or code-behind), and use the ServiceLocator within the ViewModel itself to get the services (or their interfaces) your ViewModel needs? I suggest this for two reasons:
Using the ServiceLocator in the constructor of the View for the popup will give you an error of "ServiceLocationProvider must be set" at design time within the "prism:PopupWindowAction.WindowContent" section. (Though it works fine at runtime.)
You've already been forced into a situation where you have to bypass dependency injection in some manner, so why not simplify the code, particularly if you only need access to one service anyway.
So you can do something like this:
public ItemSelectionViewModel()
{
_eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
}
And if you only need to use the IEventAggregator object once, there's no reason to even assign it to a field. Just use the ServiceLocator call where you need to get the Event Aggregator and remove your explicit constructor entirely.

How to reuse entity framework datasource for multiple views WPF EF MVVM

Hi I am designing an application using WPF4, EF and MVVM. I want to be able to create reusable UserControls that I can use in multiple windows in the application, and have them draw data from the same source.
Lets say I have a GraphView component and a TableView component that can appear on the same page or in different places in the application, and I want them to both reflect the same collection of filtered EF entities. MVVM common practice seems to require that each view has its own viewmodel. But should I be be using a joint viewmodel and bind both to it, so if you change the data or filter, both would update simultaneously? If not how should I handle this?
Thanks for any advice!
One approach could be to have two ViewModels, one for each of your Views/UserControls, and then nest them into some top or higher level ViewModel. If, for example, both Views reside in a MainWindow View, it could look like this:
public class MainWindowViewModel
{
public MainWindowViewModel(IRepository repository)
{
SharedUserControlData sharedData = new SharedUserControlData()
{
MyCollection = new ObservableCollection<MyEntity>(
repository.GetMyEntities()),
// instantiate other shared data properties
}
UserControl1ViewModel = new UserControl1ViewModel(sharedData);
UserControl2ViewModel = new UserControl2ViewModel(sharedData);
}
public UserControl1ViewModel UserControl1ViewModel { get; private set; }
public UserControl2ViewModel UserControl2ViewModel { get; private set; }
// more stuff...
}
You have a SharedUserControlData class which contains properties both views can bind to:
public class SharedUserControlData : INotifyPropertyChanged
{
public ObservableCollection<MyEntity> MyCollection { get; set; }
// other properties
// INotifyPropertyChanged implementation
}
And the ViewModels of the UserControls get those data injected:
public class UserControl1ViewModel
{
public UserControl1ViewModel(SharedUserControlData data)
{
SharedUserControlData = data;
}
public SharedUserControlData SharedUserControlData { get; private set; }
// more stuff
}
// and the same for UserControl2ViewModel
Your UserControl Views are bound to the ViewModels by a DataTemplate:
<DataTemplate DataType="{x:Type vm:UserControl1ViewModel}" >
<v:UserControl1View />
</DataTemplate>
// and the same for UserControl2ViewModel
And some controls inside of the UserControls are bound then to SharedUserControlData.MyCollection and other properties of the UserControlViewModels. The DataContext of the MainWindow is the MainWindowViewModel:
IRepository repository = new MyRepository(); // or use Dependency Injection
MainWindow window = new MainWindow();
MainWindowViewModel viewModel = new MainWindowViewModel(repository);
window.DataContext = viewModel;
In the XAML of your MainWindow we bind the UserControls to the nested ViewModels of the MainWindow's DataContext (which is the MainWindowViewModel):
<StackPanel>
<v:UserControl1View DataContext="{Binding UserControl1ViewModel}" />
<v:UserControl2View DataContext="{Binding UserControl2ViewModel}" />
</StackPanel>
This way both UserControls would have different ViewModels but both share the same SharedData instance which comes from the higher level ViewModel containing both UserControl's ViewModels. The Repository then has access to the EF data context. (Having repositories here is only an example, you could also inject instances of Service classes or something.)
Your EF classes, near as I've been able to tell after only four days using EF, reside at the project level. My first instinct would be to implement a singleton containing references to the entities you want to hold common across your viewmodels. That will create a class dependency on your singleton, of course.
This actually sounds like a design problem addressed by Unity, MEF, or something else that will do dependency injection. You'd have your EF classes in a module of one of those frameworks and use their protocols to coordinate between EF and your VM's. Then a change in your filter or your data in EF would also trigger a message your VM's could register to receive, in order to trigger UI changes or VM state changes or whatever.
I agree wholeheartedly with the one ViewModel per View approach. For shared data you can either pass references around (tedious and error prone), you can use DI (depending on your comfort level but doesn't play well with design time data), or you can create static properties in your App.xaml.cs which are then shared and accessible throughout the application. In the long run, DI will probably get the most support from other folks.
You might have a look at the BookLibrary sample application of the WPF Application Framework (WAF). It contains two different Views (BookListView [Master], BookView [Detail]) for the same data source which is provided by the Entity Framework.

Prism MVVM - How to pass an IEventAggregator to my ViewModel

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.

Resources