Rendering User Control with Abstract Base class in Design Time - wpf

I'm working on a project that has several WPF User Controls that inherit from an abstract base class (itself based on UserControl). These controls render just fine at runtime, but they don't render in the designer.
I gather that this happens because the designer attempts to create an instance of the xaml root element, in this case my base class, but it can't create an instance because it is abstract.
For the record, I know that there are "patterns & practices" type issues with having this type of control hierarchy in WPF, but refactoring the entire project is not an option currently.
My question is this: I know that there are design time attributes for setting the DataContext, DesignWidth, etc. What I'm wondering is, can you give a "design time" instance or type to be provided as a replacement when the control is loaded in the designer?

in design time Visual Studio will try to create new Instant of YourUserControl
with parameterless constructor.
if you can't create usercontrol instant like this
var myView = new MyUserControl(); //no params
the designer will fail to render.
if YourUserControl required any parameter. the most popular trick is to create dedicate constructor like this
public MyUserControl() :
this(new MockViewModel(), new MockDataContext){ } // mock Designtime constructor
puclic MyUserControl(IViewModel vm, IDataContext context) //runtime constructor
{
}
in MVVM pattern some UserControl.DataContext is user-defined Type that required some params
XAML
<UserControl.DataContext>
<local:MyViewModel />
</UserControl.DataContext>
You must define parameterless constructor for design-time environment.
public MyViewModel() : this(new MockEventAggregator()) //for designtime
{ }
[ImportingConstructor]
public MyViewModel(IEventAggregator eventAggregator) //for runtime
{
this._eventAggregator = eventAggregator;
//...
}

Related

Generic ReactiveUserControl "cannot be edited in Design view"

I changed my UserControl to be a ReactiveUserControl and now I can't view the Design View. Is there anything I can do to get the designer to work with ReactiveUserControl?
The Visual Studio designer has issues when your control or window directly inherits from a generic class. This was a pretty common issue with WinForms as well. You can work around this issue by defining another non-generic class that sits between the generic ReactiveUserControl and your control:
public partial class MyUserControl : MyUserControlBase
{
public MyUserControl()
{
InitializeComponent();
}
}
public abstract class MyUserControlBase: ReactiveUserControl<MyUserControlViewModel>
{
}
In the XAML, our root object element is defined as the base element (MyUserControlBase) and its class declaration is connected to the partial class defined above (MyUserControl):
<myNameSpace:MyUserControlBase
x:Class="MyNameSpace.MyUserControl"
xmlns:myNameSpace="clr-namespace:MyNameSpace"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

Design-Time setup of a ViewModel

I'm using Visual Studio 2013's designer to create my User Control in WPF, and I'm using a MVVM approach.
I'm trying to find the best way to have "Design-Time" setup of my viewmodel so that I immediatly see the effect in the designer of changing a value of a property for instance. I've used different designs and techniques to support this, but nothing is exactly what I want. I'm wondering if someone has better ideas...
Situation (simplified):
So I have a "Device" which I want a UserControl to show states and operations. From top to bottom:
I have a IDeviceModel which has a field bool IsConnected {get;} (and proper notification of state changes)
I have a FakeDeviceModel which implements IDeviceModel, and thus enables me to not rely on a real device for design-time and testing
A DeviceViewModel, which contains a IDeviceModel, and encapsulate the model's properties. (yes it has proper INotifyPropertyChanged notifications in it)
My UserControl which will have a DataContext of type DeviceViewModel, and would have a custom-styled CheckBox which is IsChecked={Binding IsConnected, Mode=OneWay
My Goal: I want to preview on design time how does the Model's IsConnected state affect my UserControl (So it could affect other things than just IsChecked)
Framework:
I use the idea of the MVVM Light ViewModelLocator, returning non-static fields (so new instances of ViewModels). At runtime, the real datacontext will be given by the one instanciating this UserControl
d:DataContext="{Binding DeviceViewModelDesignTime, Source={StaticResource ViewModelLocator}}"
public class ViewModelLocator
{
private static MainWindowViewModel _mainWindowViewModel;
public MainWindowViewModel MainWindowViewModelMainInstance
{
get
{
if (_mainWindowViewModel == null)
{
_mainWindowViewModel = new MainWindowViewModel();
}
return _mainWindowViewModel;
}
}
public DeviceViewModel DeviceViewModelDesignTime
{
get
{
//Custom initialization of the dependencies here
//Could be to create a FakeDeviceModel and assign to constructor
var deviceViewModel = new DeviceViewModel();
//Custom setup of the ViewModel possible here
//Could be: deviceViewModel.Model = new FakeDeviceModel();
return deviceViewModel;
}
}
Solutions I tried:
Compile-Time solution
Simply code the setup of the ViewModel in the ViewModelLocator.
var deviceViewModel = new DeviceViewModel(fakeDeviceModel);
var fakeDeviceModel = new FakeDeviceModel();
fakeDeviceModel.IsConnected = true;
deviceViewModel.AddDevice(fakeDeviceModel);
Pros: Simple
Cons: That's longer iterations of always going to change the value in code, recompile, go back to designer view, wait for result
Instance in resources and kept static in ViewModelLocator
So I create an instance in XAML and I try to push it in the current ViewModel used by the designer. Not the cleanest way, but worked for a while in simple situation (yes there's some wierdness with the collection, but was with the idea that I could have multiple devices and a current one)
XAML:
<UserControl x:Class="Views.StepExecuteView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{Binding DeviceViewModelDesignTime, Source={StaticResource ViewModelLocator}}">
<UserControl.Resources>
<viewModels:DesignTimeDeviceManager x:Key="DesignTimeDeviceManager">
<viewModels:DesignTimeDeviceManager.DesignTimeDevices>
<device:FakeDeviceModel IsConnected="True"
IsBusy="False"
IsTrayOpen="True"
NumberOfChipSlots="4"
/>
</viewModels:DesignTimeDeviceManager.DesignTimeDevices>
[... CheckBox binding to datacontext and so on...]
And ViewModelLocator.cs:
public class ViewModelLocator
{
private static MainWindowViewModel _mainWindowViewModel;
public MainWindowViewModel MainWindowViewModelMainInstance
{
get
{
if (_mainWindowViewModel == null)
{
_mainWindowViewModel = new MainWindowViewModel();
}
return _mainWindowViewModel;
}
}
public static FakeDeviceModel DeviceModelToAddInDesignTime;
public DeviceViewModel DeviceViewModelDesignTime
{
get
{
var deviceViewModel = new DeviceViewModel();
if (DeviceModelToAddInDesignTime != null)
deviceViewModel.AddDevice(DeviceModelToAddInDesignTime );
return deviceViewModel;
}
}
}
public class DesignTimeDeviceManager
{
private ObservableCollection<FakeDeviceModel> _DesignTimeDevices;
public ObservableCollection<FakeDeviceModel> DesignTimeDevices
{
get { return _DesignTimeDevices; }
set
{
if (_DesignTimeDevices != value)
{
_DesignTimeDevices = value;
ViewModelLocator.DeviceModelToAddInDesignTime = value.FirstOrDefault();
}
}
}
}
Pros:
Worked super great on one project. the instance that I had in XAML, I could modify the booleans and I would get -immediate- feedback on how it affects my UserControl. So in the simple situation, the CheckBox's "Checked" state would change and I could modify my styling in real-time, without needing to recompile
Cons:
It stopped working in another project, and this by itself I couldn't find the reason why. But after recompiling and changing stuff, the designer would give me exceptions looking like "Cannot cast "FakeDeviceModel" to "FakeDeviceModel""!! My guess is that the Designer internally compiles and uses caches for those types (C:\Users\firstname.lastname\AppData\Local\Microsoft\VisualStudio\12.0\Designer\ShadowCache). And that in my solution, depending on the ordering of things, I was creating a "FakeDeviceModel" which was assigned to a static instances, and "later on", the next time the ViewModelLocator would be asked for a ViewModel, it would use that instance. However, if in the meantime he "recompiles" or uses a different cache, then it's not "exactly" the same type. So I had to kill the designer (XDescProc) and recompile for it to work, and then fail again a few minutes after. If someone can correct me on this it would be great.
Multi-Binding for d:DataContext and custom converter
The previous solution's problem was pointing me to the fact that the ViewModel and the FakeDeviceModel were created at different moment in time (giving the type/cast problem) and to solve it, I would need to create them at the same time
XAML:
<UserControl x:Class="MeltingControl.Views.DeviceTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<d:UserControl.DataContext>
<MultiBinding Converter="{StaticResource DeviceDataContextConverter}">
<Binding Path="DeviceViewModelDesignTime" Source="{StaticResource ViewModelLocator}" />
<Binding>
<Binding.Source>
<device:FakeDeviceModel IsConnected="False"
IsBusy="False"
IsTrayOpen="False"
SerialNumber="DesignTimeSerie"
/>
</Binding.Source>
</Binding>
</MultiBinding>
</d:UserControl.DataContext>
public class DeviceDataContextConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length == 0)
return null;
var vm = (DeviceViewModel)values[0];
if (values.Length >= 2)
{
var device = (IDeviceModel)values[1];
vm.AddDevice(device);
}
return vm;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Pros:
-Works super nice! When the binding for the DataContext asks for the ViewModel, I take advantage of the Converter to modify that ViewModel and inject my device before returning it
Cons:
We lose intelissense (with ReSharper), since he doesn't know what type is returned by the converter
Any other ideas or modifications I could make to solve this issue?
You may create a design time ViewModel that returns IsConnected = true based on your view mode (FakeDeviceViewModel) and then set it as a design-time data context:
d:DataContext="{d:DesignInstance viewModels:FakeDeviceViewModel,
IsDesignTimeCreatable=True}"
Where viewModels: is the xaml namespace to the actual view model.
I've documented exactly how I managed to get the perfect setup to see live Design Time data in Visual Studio.
Look at Hint 9 - Design Time DataContext on this page:
ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext"
I would like to propose an alternative solution.
You could use that same view model for design time data and normal runtime, and check in your (single) viewmodel whether the designer is active and then load the design time data there.
In your view model you would do something like this:
public class ExampleViewModel : ViewModelBase
{
public ExampleViewModel()
{
if (IsInDesignMode == true)
{
LoadDesignTimeData();
}
}
private void LoadDesignTimeData()
{
// Load design time data here
}
}
The IsInDesignMode property could be placed in your view model base class - if you have one - and looks like this:
DesignerProperties.GetIsInDesignMode(new DependencyObject());
Please take a look at my answer here
This is how I am doing it one of my projects using MVVMLight.
Create interface for every viewmodel for which you need separate design time and run time properties and behavior.
Make separate viewmodels for every view - one for run time and another for design time. Derive both viewmodels from the same interface defined above.
Create a static class that has two static methods - one for registering services for run time in the IOC container and another for registering services for design time in the IOC container. I use the same SimpleIOC.Default container. Register appropriate viewmodels in both the methods bound to their interfaces.
public static class MyServiceLocator()
{
public static void RegisterRunTimeServices()
{
ServiceLocator.SetLocatorProvider(() => SimpleIOC.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<IAboutViewModel, AboutViewModel>();
}
public static void RegisterDesignTimeServices()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<IAboutViewModel, DesignTimeAboutViewModel>();
}
In constructor of ViewModelLocator, check if the app is in DesignMode and accordingly call the static method to register services.
public ViewModelLocator()
{
if (ViewModelBase.IsInDesignModeStatic)
{
MyServiceLocator.RegisterDesignTimeServices();
}
else MyServiceLocator.RegisterRunTimeServices();
}
Now, your Views just have to set datacontext as corresponding viewmodel interface instead of viewmodel object. To do that, instead of exposing viewmodel objects to each view from the ViewModelLocator, expose viewmodel interface.
In ViewModelLocator.cs
public IAboutViewModel About
{
get
{
return ServiceLocator.Current.GetInstance<IAboutViewModel>();
}
}
In AboutView.xaml
DataContext="{Binding Source={StaticResource Locator}, Path=About}"
Wherever needed in code, cast the interface to ViewModelBase type to convert it to ViewModel object and use.
In MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private readonly IAboutViewModel _aboutViewModel;
public MainViewModel()
{
_aboutViewModel = ServiceLocator.Current.GetInstance<IAboutViewModel>();
CurrentViewModel = (ViewModelBase) _aboutViewModel;
}
}
So, basically I use DI to inject appropriate viewmodels to each view depending on whether the code is in run time or design time. The ViewModelLocator simply registers either design time or run time viewmodels in the SimpleIOC container. The benefit of this is that there is no mixing of code in viewmodel files and one can also setup the code for multiple design time data without much interference. If you want designtime data to show while the application runs, then also its possible with one line change in code.
I would create an instance of Fake View Model in a separate xaml e.g. DeviceViewModelDataSample.xaml (see example below)
Set Build Action to DesignData
Reference the file as such
<UserControl x:Class="YourNameSpace.YourControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignData Source=/DataSample/DeviceViewModelDataSample.xaml}">
<!-- Skiped details for brevity -->
</UserControl>
DeviceViewModelDataSample.xaml
<vm:DeviceViewModel xmlns:dm="clr-namespace:YourNameSpace.DataModel"
xmlns:vm="clr-namespace:YourNameSpace.ViewModel">
<vm:DeviceViewModel.DeviceManager> <!-- Assuming this is a collection -->
<dm:DeviceModel DeviceName="Fake Device" IsConnected ="true" /> <!-- This creates an instance at design time -->
</vm:DeviceViewModel.DeviceManager>
</vm:DeviceViewModel>

How to use Caliburn Micro in a WinForms app with one WPF form

We have a (massive) legacy WinForms app which, through a menu item, opens up a WPF form. This WPF form will host an Infragistics grid, and some buttons/drop-downs.
This lone WPF form represents the nascent stage of a migration to WPF. Later on, more components of the app will move to WPF, and ultimately the entire app itself.
As part of the migration, we would like to use Caliburn Micro. Hence, it would be nice if we could start by using it with this lone WPF form.
Can someone please provide some pointers on how to use Caliburn Micro with the WPF form?
Or perhaps tell me why it may not make sense to use Caliburn Micro just yet?
The documentation I've read so far involves boot strappers that ensure the application starts with the desired root view model, rather than the scenario above.
Many thanks!
After much Googling and going through the Caliburn Micro source code, I've come up with an approach that works in a sample test application. I can't post the test application here for certain reasons, but here's the approach in a nutshell.
Create a WinForm with a button.
On button click, show a ChildWinForm
In the load handler of the ChildWinForm:
// You'll need to reference WindowsFormsIntegration for the ElementHost class
// ElementHost acts as the "intermediary" between WinForms and WPF once its Child
// property is set to the WPF control. This is done in the Bootstrapper below.
var elementHost = new ElementHost{Dock = DockStyle.Fill};
Controls.Add(elementHost);
new WpfControlViewBootstrapper(elementHost);
The bootstrapper above is something you'll have to write.
For more information about all it needs to do, see Customizing the Bootstrapper from the Caliburn Micro documentation.
For the purposes of this post, make it derive from the Caliburn Bootstrapper class.
It should do the following in its constructor:
// Since this is a WinForms app with some WPF controls, there is no Application.
// Supplying false in the base prevents Caliburn Micro from looking
// for the Application and hooking up to Application.Startup
protected WinFormsBootstrapper(ElementHost elementHost) : base(false)
{
// container is your preferred DI container
var rootViewModel = container.Resolve();
// ViewLocator is a Caliburn class for mapping views to view models
var rootView = ViewLocator.LocateForModel(rootViewModel, null, null);
// Set elementHost child as mentioned earlier
elementHost.Child = rootView;
}
Last thing to note is that you'll have to set the cal:Bind.Model dependency property in the XAML of WpfControlView.
cal:Bind.Model="WpfControls.ViewModels.WpfControl1ViewModel"
The value of the dependency property is used passed as a string to Bootstrapper.GetInstance(Type serviceType, string key), which must then use it to resolve the WpfControlViewModel.
Since the container I use (Autofac), doesn't support string-only resolution, I chose to set the property to the fully qualified name of the view model. This name can then be converted to the type, and used to resolve from the container.
Following up on the accepted answer (good one!), I'd like to show you how to implement the WinForms Bootstrapper in a ViewModel First approach, in a way that:
You won't have to create a WPF Window and,
You won't have to bind directly to a ViewModel from within a View.
For this we need to create our own version of WindowManager, make sure we do not call the Show method on the Window (if applicable to your case), and allow for the binding to occur.
Here is the full code:
public class WinformsCaliburnBootstrapper<TViewModel> : BootstrapperBase where TViewModel : class
{
private UserControl rootView;
public WinformsCaliburnBootstrapper(ElementHost host)
: base(false)
{
this.rootView = new UserControl();
rootView.Loaded += rootView_Loaded;
host.Child = this.rootView;
Start();
}
void rootView_Loaded(object sender, RoutedEventArgs e)
{
DisplayRootViewFor<TViewModel>();
}
protected override object GetInstance(Type service, string key)
{
if (service == typeof(IWindowManager))
{
service = typeof(UserControlWindowManager<TViewModel>);
return new UserControlWindowManager<TViewModel>(rootView);
}
return Activator.CreateInstance(service);
}
private class UserControlWindowManager<TViewModel> : WindowManager where TViewModel : class
{
UserControl rootView;
public UserControlWindowManager(UserControl rootView)
{
this.rootView = rootView;
}
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
if (isDialog) //allow normal behavior for dialog windows.
return base.CreateWindow(rootModel, isDialog, context, settings);
rootView.Content = ViewLocator.LocateForModel(rootModel, null, context);
rootView.SetValue(View.IsGeneratedProperty, true);
ViewModelBinder.Bind(rootModel, rootView, context);
return null;
}
public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
CreateWindow(rootModel, false, context, settings); //.Show(); omitted on purpose
}
}
}
I hope this helps someone with the same needs. It sure saved me.
Here are somethings you can start with
Create ViewModels and inherit them from PropertyChangedBase class provided by CM framework.
If required use the EventAggregator impelmentation for loosly coupled communication \ integration
Implement AppBootStrapper without the generic implementation which defines the root view model.
Now you can use the view first approach and bind the view to model using the Bind.Model attached property on view. I have created a sample application to describe the approach here.

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.

WPF + MvvM + Prism

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);

Resources