I've created a custom view model locator using Autofac and set it up normally through the App.xaml like most of them are usually used. My problem is how do I unit test now? I'm getting an error every time I try to test a method that initializes a view
In my app.xaml:
<desktop:ViewModelLocator xmlns:local="clr-namespace:MyProject.Desktop" x:Key="ViewModelLocator" />
In each view:
DataContext="{Binding MyFirstViewModel, Source={StaticResource ViewModelLocator}}"
The Unit Test Error:
{"Cannot find resource named 'ViewModelLocator'. Resource names are case sensitive."}
I understand why cause when you unit test, there really isn't an instance of the actual App so what is a good way around this problem?
ViewModelLocator Code:
/// <summary>
/// Autofac object container
/// </summary>
private readonly IContainer objectContainer;
#region Constructor
/// <summary>
/// Constructor for view model locator
/// </summary>
public ViewModelLocator()
{
objectContainer = App.ObjectContainer;
//objectContainer.BeginLifetimeScope();
}
#endregion
#region Properties
/// <summary>
/// Gets the resolved instance of a main window view model
/// </summary>
public MainWindowViewModel MainWindowViewModel
{
get
{
return objectContainer.Resolve<MainWindowViewModel>();
}
}
public FirstViewModel MyFirstViewModel
{
get
{
return objectContainer.Resolve<FirstViewModel>();
}
}
public SecondViewModel MySecondViewModel
{
get
{
return objectContainer.Resolve<SecondViewModel>();
}
}
This is a bit late, but maybe useful. Instead of resolving objectContainer in the constructor, do it through the property:
//note this is a lazy getter, i.e. will be resolved when needed on the first call
private IContainer ObjectContainer
{
get
{
if(objectContainer == null)
objectContainer = App.ObjectContainer;
return objectContainer:
}
}
Then use the property through your code, not the field. Also when I am concerned about someone else using the field that I want to enforce through the property usage, I would rename it to something that would not easily be recognizable in the IntelliSence (zREgdnlksfObjectContainer for example:) ) Note the property is private, so nothing really changes. You can make the property internal and mark your lib to be visible to your unit test, so that in the unit test you can Mock it to WhenCalled() return/resolve IContainer.
Related
I have a 2-levels hierarchy in my model composed of constellations and entities, hosted in a root object, and I want to show them in a TreeView.
Root object
L Constellations
L Entities
I have my RootObjectViewModel exposing my root object. In that, I fully agree with Bryan Lagunas in MVVM best practices, that the model object should be exposed by the view model instead of doing facade. Excellent video by the way, really worth the time.
I read everywhere that the TreeView.ItemsSource should be mapped to a collection of viewmodels, that is, for my collection of Constellation, my RootObjectViewModel should provide a collection of ConstellationViewModel.
My concern is that if my collections of constellations, and entities within, are live, that is, if some items are added, changed (their order) or removed, I have to manually reflect those changes in my ViewModels' collections.
I would find it more elegant to map the ItemsSource to, say, the collection of Constellation itself, so that any change in the model is reflected without duplication, and then have some converter or so to map the TreeViewItems.DataContext to a ConstellationViewModel based on the constellation model.
If this is not clear enough, I can clarify this with some sample code.
Did anyone face the same question and/or try to implement this ?
Thanks you in advance for your help.
Cedric
It depends. If your model has exactly the properties the view needs, and the view can directly alter them when the user clicks around, it's fine to expose the model.
But if your model is, for example, read only and require calls to a service to apply changes, you have to wrap it in a view model to provide the view with writeable properties.
Got it working !
It is not possible out-of-the-box, and here's why:
It is possible to use model collections as items source, and to use a converter to get the appropriate view model it the components inside the TreeViewItem. But there isn't any way to interfere with the creation of TreeViewItem to apply the converter to its DataContext. Which means that the TreeViewItem's properties can't be binded to the ViewModel.
In other words :
if you want to stick with the standard behavior of the TreeView and don't have to deal with the TreeViewItems properties, if either your collections don't change or can implement ICollectionChanged, and if your models don't change or can implement IPropertyChanged, it is fine to go with the model's collections.
If any of these conditions is broken, then you will have to go with building ViewModel's collections and sync them with model's collection.
Now, I implemented a collection type named ConvertingCollection<Tin, Tout> that uses an original collection as an input and syncs its own contents with this input. This is just a basic class with many many ways of improvement, but it works. All you have to do is use this collection as a VM property, set the original collection and converter, and bind the ItemsSource to this collection.
Here's the full code:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Windows.Data;
namespace TreeViewPOC
{
class ConvertingCollection<Tin, Tout> : ObservableCollection<Tout>
{
private IValueConverter _converter;
private bool _isObservableCollection;
private IEnumerable<Tin> _originalCollection;
private Dictionary<Tin, Tout> _mapping = new Dictionary<Tin, Tout>();
public ConvertingCollection(IValueConverter converter)
{
// save parameters
_converter = converter;
}
public ConvertingCollection(IEnumerable<Tin> originalCollection, IValueConverter converter)
{
// save parameters
_converter = converter;
OriginalCollection = originalCollection;
}
#region Properties
public IEnumerable<Tin> OriginalCollection
{
get
{
return _originalCollection;
}
set
{
if (!value.Equals(_originalCollection))
{
// manage older collection
if (_originalCollection != null && _isObservableCollection)
{
(_originalCollection as ObservableCollection<Tin>).CollectionChanged -= originalCollection_CollectionChanged;
this.Clear();
}
_originalCollection = value;
// setup original collection information.
_isObservableCollection = _originalCollection is INotifyCollectionChanged;
if (_originalCollection != null && _isObservableCollection)
{
(_originalCollection as INotifyCollectionChanged).CollectionChanged += originalCollection_CollectionChanged;
foreach (Tin item in _originalCollection)
{
AddConverted(item);
}
}
}
}
}
#endregion
/// <summary>
/// Indicates the time in milliseconds between two refreshes.
/// </summary>
/// <notes>
/// When the original collection isn't observable, it must be explored to reflect changes in the converted collection.
/// </notes>
// TODO
//public int RefreshRate { get; set; } = 1000;
/// <summary>
/// Flushes the collection.
/// </summary>
public new void Clear()
{
_mapping.Clear();
base.Clear();
}
#region Events management
private void originalCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Tin item in e.NewItems)
{
AddConverted(item);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Tin item in e.OldItems)
{
RemoveConverted(item);
}
break;
}
}
#endregion
#region Helpers
/// <summary>
/// Converts an item and adds it to the collection.
/// </summary>
/// <param name="item">The original item.</param>
private void AddConverted(Tin item)
{
Tout converted = (Tout) _converter.Convert(item, typeof(Tout), null, CultureInfo.CurrentCulture);
_mapping.Add(item, converted);
this.Add(converted);
}
/// <summary>
/// Removes a converted itemfrom the collection based on its original value.
/// </summary>
/// <param name="item">The original item.</param>
private void RemoveConverted(Tin item)
{
this.Remove(_mapping[item]);
_mapping.Remove(item);
}
#endregion
}
}
I created a project on Github named MVVMTreeViewPOC to POC the idea. It is working fine, and it is a good tool to show the limitations.
Cedric.
I'm using Catel in my application. I have any questions regarding DataObjects and ViewModels - what is the best way to use Catel efficientlu?
Scenario 1:
I have a MainViewModel and a MainView. In this View I call another View (DataWindow) with a own ViewModel (SettingsViewModel) and show it in a Dialog. In this Dialog I insert some SettingsValues and store it in xml. Last but not least I have a DataObject class to store the data from the Dialog. Here any pseudocode:
MainViewModel : ViewModelBase
{
private void OnSystemSettingsCommandExecute()
{
//create a new ViewModel and show as Dialog
uiVisualizerService.ShowDialog(new SystemSettingsViewModel());
}
...
}
SystemSettingsViewModel : ViewModelBase
{
/// <summary>
/// Gets or sets the property value.
/// </summary>
[Model]
public SettingsDataObject SettingsData
{
get { return GetValue<SettingsDataObject>(SettingsDataProperty); }
set { SetValue(SettingsDataProperty, value); }
}
/// <summary>
///
/// </summary>
public static readonly PropertyData SettingsDataProperty = RegisterProperty("SettingsData", typeof(SettingsDataObject));
/// <summary>
/// It is right to define the property again here?
/// </summary>
[ViewModelToModel("SettingsData")]
public string UserName
{
get { return GetValue<string>(UserNameProperty); }
set { SetValue(UserNameProperty, value); }
}
/// <summary>
/// Register the UserName property so it is known in the class.
/// </summary>
public static readonly PropertyData UserNameProperty = RegisterProperty("UserName", typeof(string));
// Load and Save right here?
protected override bool Save()
{
SettingsData.Save(#"D:\Projects\Testdata\xml\Settings.xml");
return true;
}
protected override void Initialize()
{
SettingsData = SavableModelBase<SettingsDataObject>.Load(#"D:\Projects\Testdata\xml\Settings.xml", SerializationMode.Xml);
}
}
public class SettingsDataObject : SavableModelBase<SettingsDataObject>
{
// Propertys
/// <summary>
/// Gets or sets the property value.
/// </summary>
public string UserName
{
get { return GetValue<string>(UserNameProperty); }
set { SetValue(UserNameProperty, value); }
}
/// <summary>
/// Register the UserName property so it is known in the class.
/// </summary>
public static readonly PropertyData UserNameProperty = RegisterProperty("UserName", typeof(string), "MyUserName");
}
Is it right that I must define the property "UserName" in the DataClass and in the ViewModel class? Is that the "normal way" that I define my Model property in the ViewModel and than i access my data propertys with [ViewModelToModel("SettingsData")]?
How I can Load and Save automatic my Data Objects? In my case I override the "Save" and the "Initialize" methode? Is there a better way to do this in Catel?
Now I must have access of the SettingsDataObject in the MainViewModel but i didn't find a way to use the object in other ViewModels. What is the "best practices" to load the settings in other ViewModels?
Question 1)
Yes, this is "right", but totally depends on the form of MVVM you want to follow. For more information, read this. I like to protect my model (make it private on the VM) and only expose the properties I really want being exposed on the VM. But some other people like to directly bind on the model instead. It's just the different number of interpretations like you can read in the article.
Question 2)
Overriding the Initialize and Save are exactly meant for this, so you are doing it the right way!
Question 3)
When you need to share a model, you can either use nested user controls to pass the model from one view model to another. If you need to access the model in a lot of different places, it might be wise to register it in the ServiceLocator. It can then automatically be injected into your view models by Catel.
// Somewhere in your code
var serviceLocator = ServiceLocator.Default;
serviceLocator.RegisterType<ISettings>(mySettings);
// Your view model constructor
public MyViewModel(ISettings mySettings)
{
// injected here
}
If you want to create your own view model, you can do this:
var dependencyResolver = this.GetDependencyResolver();
var viewModelFactory = dependencyResolver.Resolve<IViewModelFactory>();
var viewModel = viewModelFactory.CreateViewModel<MyViewModel>(null);
Note that if you are inside another VM, you can of course let the IViewModelFactory be injected so you only need 1 line of code to create the view model.
How can we bind a user-control to a view-model object, when this last contains parameters in his constructor ???
Does the binding using "DataContext" in the view ensure that when we create a view-model, the view is automatically created ??
If you are using an IoC container, this is supported out-of-the-box.
It really depends on the IoC container you are using, but here is an example using Prism Unity container.
The following examples are taken out from the Prism QuickStarts guide
So, at first, we will have to set up the unity container:
public class QuickStartBootstrapper : UnityBootstrapper
{
private readonly CallbackLogger callbackLogger = new CallbackLogger();
/// <summary>
/// Configures the <see cref="IUnityContainer"/>.
///May be overwritten in a derived class to add specific
/// type mappings required by the application.
/// </summary>
protected override void ConfigureContainer()
{
// Here you can do custom registeration of specific types and instances
// For example
this.Container.RegisterInstance<CallbackLogger>(this.callbackLogger);
base.ConfigureContainer();
}
}
Baisically, youre done!
All you have to do now is have your view recieve the viewModel as a parameter in his constructor, like this:
public partial class OverviewView
{
public OverviewView(OverviewViewModel viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
}
Unity IoC container will take care of your parameters in the ViewModel even without you having to register those types most of the times.
Please note that in my answer I only refered to the IoC part of the configuration. setting up an entire MVVM application requires a bit more work and varies depending the MVVM framework you are using
I have a custom UserControl which exposes the following dependency property: CanEdit. The property was created using a snippet and the generated code is:
#region CanEdit
/// <summary>
/// CanEdit Dependency Property
/// </summary>
public static readonly DependencyProperty CanEditProperty =
DependencyProperty.Register("CanEdit", typeof(bool), typeof(RequisitionItem),
new PropertyMetadata((bool)false));
/// <summary>
/// Gets or sets the CanEdit property. This dependency property
/// indicates ....
/// </summary>
public bool CanEdit {
get { return (bool)GetValue(CanEditProperty); }
set { SetValue(CanEditProperty, value); }
}
#endregion
I'm trying to set this property to True on the parent UserControl, like this:
<RequisitionItem CanEdit="True" />
but the property stays False.
Why is that?
Assuming that you mean the child items have the property still set to false this sounds like an inheritance issue.
See this page on value inheritance, there is one section called Making a Custom Property Inheritable, which might offer some help.
I start using WPF with PRISM and MVVM. One problem I am facing is that I can’t find a good place / best practice to unsubscribe EventAggregator events formerly subscribed in the ViewModel. The following solution - calling Unsubscribe in the destructor – is much too late. It is just running with the next garbage collection.
public class ViewModel : ViewModelBase
{
public ViewModel()
{
var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<SeriesSelectionChangedEvent>().Subscribe(OnSeriesSelectionChanged);
}
~ViewModel()
{
var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<SeriesSelectionChangedEvent>().Unsubscribe(OnSeriesSelectionChanged);
}
void OnSeriesSelectionChanged(SeriesSelectionChangedEventArgs e)
{
}
}
It's up to you! If your application can notify ViewModel when it is no longer needed, so you should unsubscribe there.
For example, in our project we have IViewDisposeService. If view (or its model) needs deterministic finalization, it registers itself in IViewDisposeService when showing. Then the Core use the same service to notify registered views when they've been removed from regions.
Another way is to use commands. Your model expose command which must be invoked by a view when it is closing. ViewModel may use command handler to unsubscribe.
By the way, if you worry that EventAggregator will hold your ViewModel, it's not a problem, because Prism's EventAggregator use weak references.
Well sometime back, I also faced the same issue. Here's what we did (WPF App).
Create a new base class - DisposableUserControl : UserControl, IDisposable. This will contain the logic of disposing an user control. Code added in the end.
Replace all user control in your application with DisposableUserControl. like < app: DisposableUserControl .... > < / app.DisposableUserControl>
Add an OnDispose method (Virtual) in ViewModelBase which is called in Dispose () method of VM.Each ViewModel of your app should override this OnDispose method in which you'll unsubscribe your events. Something like-
OnDispose() { base.Dispose(); UnsubscribeEvent (abcEventSubscribername); }
Code
/// <summary>
/// Falg used to avoid calling dispose multiple times on same user control
/// </summary>
private bool isDisposed;
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// If disposing equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed. If disposing equals false, the method has been called by the
/// runtime from inside the finalizer and you should not reference
/// other objects, only unmanaged resources can be disposed.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this.isDisposed)
{
this.isDisposed = true;
if (disposing)
{
UtilityFunctions.DisposeChildDisposableUserControls(this);
if (this.DataContext != null && this.DataContext is IDisposable)
{
var parent = LogicalTreeHelper.GetParent(this);
if (parent == null || ((parent as FrameworkElement).DataContext != this.DataContext))
{
(this.DataContext as IDisposable).Dispose();
}
BindingOperations.ClearAllBindings(this);
this.DataContext = null;
}
}
}
}
You could have the View notify the ViewModel when it is unloaded (or in case of a Window when it is closed). Then in the Unloaded/Closed handler in the ViewModel you could unsubscribe. That is the way I do it in my application.