Im trying to get to grips with writing testable ViewModels in Silverlight 4. Im currently using MVVM light.
Im using AutoFac and the IoCContainer is doing its job fine. However to inject into the constructor of ViewModels, which are bound to Views I have this constructor chaining:
public UserViewModel() : this(IoCContainer.Resolve<IUserServiceAsync>())
{
}
public UserViewModel(IUserServiceAsync userService)
{
if (this.IsInDesignMode) return;
_userService = userService;
}
Which doesn't feel clean, but is the best option i have found so far. This works and my app works as desired, and is testable with control inverted.
However, with my VM bound to my view like this:
<UserControl.DataContext>
<ViewModel:UserViewModel />
</UserControl.DataContext>
In my XAML page attributes, design mode in both VS2010 and Blend doesnt work.
Is there are nicer way to achieve what im trying in Silverlight that still works with design mode? Losing design mode isnt a deal breaker but will be handy while learning XAML. A cleaner none chained way would be nice though!
Im thinking it maybe possible to use AutoFac / IoC to resolve viewmodels to views, as apposed to the XAML mark-up approach above, and go down this route?
Thanks.
Instead of implementing the first constructor, I suggest you implement a ViewModelLocator, like this:
public class ViewModelLocator
{
IoCContainer Container { get; set; }
public IUserViewModel UserViewModel
{
get
{
return IoCContainer.Resolve<IUserViewModel>();
}
}
}
Then in XAML you declare the locator as a static resource:
<local:ViewModelLocator x:Key="ViewModelLocator"/>
While you initialize your application, it is necessary to provide the locator with the instance of the container:
var viewModelLocator = Application.Current.Resources["ViewModelLocator"] as ViewModelLocator;
if(viewModelLocator == null) { // throw exception here }
viewModelLocator.Container = IoCContainer;
Then in XAML you can use the resource cleanly:
<UserControl
DataContext="{Binding Path=UserViewModel, Source={StaticResource ViewModelLocator}}"
/>
<!-- The other user control properties -->
For design time, you can implement a MockViewModelLocator:
public class MockViewModelLocator
{
public IUserViewModel UserViewModel
{
get
{
return new MockUserViewModel();
}
}
}
Declare it in XAML appropriately:
<local:MockViewModelLocator x:Key="MockViewModelLocator"/>
And finally use it in your user control:
<UserControl
d:DataContext="{Binding Path=UserViewModel, Source={StaticResource MockViewModelLocator}}"
DataContext="{Binding Path=UserViewModel, Source={StaticResource ViewModelLocator}}"
/>
<!-- The other user control properties -->
You can make your mock view model locator return safe and easily readable data for Blend to use and during runtime you will be using your real service.
This way you do not lose design time data and you do not have to sacrifice the cleanliness of the dependency injection methodology in your view models.
I hope this helps.
Related
I'm doing a paper on code-design and I'm comparing MV-C,P and VM to see which one is the best fit for WPF. During my research I realised that MVVM is the obvious choice cause of the databinding among other criterias.
Even If I know this I have to "prove" this in a sense so I'm creating an application both in MVP and MVVM that does exactly the same thing but handles code differently. With this I will explain the pros and cons with these code-patterns but in my creating with the MVP-application I reached a problem.
I have a model with the "buisness-logic" and my presenter creates a list of these model objects that my view can display.
My problem is how I display them
In MVVM I bind my list to a ListBox cause that is how MVVM is "made" to do.
EG
<Window.Resources>
<DataTemplate DataType="{x:type model:Mymodel}">
//Some DataTemplate Definition
</DataTemplate>
</Window.Resources>
Then binding to my Listbox
<ListBox ItemSources={Binding someProperty} />
It's not fully coded but you get the gesture
But if I've understood correctly, binding with MVP is not how it should be.
You should not bind anything in MVP cause that is not how it is supposed to work, or am I wrong here?
So if I shouldn't bind data, how can I display this list of my model objects in a ListBox so it doesn't say
Model Object
Model Object
Model Object
Model Object
I understand that you should use MVVM for WPF but for the sake of proving why it's better I need to show how MVP can work in WPF also.
When you're using WPF, it is as you said made to work with MVVM, trough the data-binding. MVP was often used with Windows-form where no data-binding was available. If you want your applications to have the same functionality and use the same technology (WPF) you can't avoid using binding or it's at least more difficult to do. As long as you talk through the presenter to your model you're still using MVP. You can decide for yourself if you want to use
Passive View - The presenter handles ALL dialog between the view and model
SuperVising Presenter - The View knows about the model and the presenter handles "difficult-code" that is to much to be handled between the view and model.
If you're using binding I would say (Unsure about it) you're using SuperVising Presenter which is not "recommended", but using MVP in WPF is not recommended either so...
EDIT Example
For instance if you want to display a list you need to have an interface that has a property of a list containing objects you want to display.
public interface myinterface
{
ObservableCollection<YourModel> ListName {get; set;}
}
and then in your presenter just "push" the data to that list
private myinterface _my;
public Presenter(myinterface my)
{ this._my = my;}
_my.ListName = // Add whatever Data you want into this list.
And in your view
<ListBox ItemSource ={Binding ListName}>
<ListBox.ItemTemplate>Set how you want to display the list</ListBox.ItemTemplate>
This is a unclear example but hopefully can give you the idea how MVP works with WPF ( in a small way)
I cannot add as much code as I want to the comment so I'll post an answer. If something is unclear just give me a feedback so I'll give you more details.
From the example you've shown I would go about and make binding from View to Presenter which should be the bridge between View and Model as you can see in here :
( Image from wikipedia article )
View should send events/changes to the presenter and presenter should be the "brain/logic" of the View which decides if it should update the Model or not.
Assuming you've the View like this one :
<UserControl x:Class="EntryNamespace.MeView"
... >
<!-- ListItems should return collection of Presenters -->
<ListView ItemsSource="{Binding ListItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- Elementcontent should be a property inside Presenter that returns value from Model -->
<Button Content="{Binding ElementContent}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
You can create a Presenter like this one :
class Presenter : INotifyPropertyChanged
{
public ObservableCollection<ListItemPresenter> ListItems
{
get { return GetItems(); }
set { SetItems(value); }
}
ObservableCollection<ListItemPresenter> GetItems()
{
// private logic to retrieve `ListItemPresenter` collection from model
var collection = new ObservableCollection<ListItemPresenter>();
foreach(ListItem listItem in Model.Items)
collection.Add(listItem.GetPresenter());
return collection;
}
void SetItems(ObservableCollection<ListItemPresenter> objects)
{
// private logic to transfer `ListItemPresenter` collection to model
// remember to call NotifyPropertyChanged("ListItems");
Model.Items.Clear();
foreach(ListItemPresenter presenter in objects)
Model.Items.Add(presenter.GetModel());
NotifyPropertyChanged("ListItems");
}
}
You Model can look like this:
public class ListItem
{
ListItemPresenter m_Presenter;
public ListItemPresenter GetPresenter()
{
return m_Presenter;
}
string m_ElementContent;
public string ElementContent
{
get { return m_ElementContent; }
set { m_ElementContent = value; }
}
public ListItem(ListItemPresenter presenter)
{
m_Presenter = presenter;
}
}
Another way to use Presenter can look like such :
Assuming you've similar View just create a Presenter:
public class Presenter
{
ObservableCollection<ListItem> m_ListItems = new ObservableCollection<ListItem>();
public ObservableCollection<List> ListItems
{
get { return m_ListItems; }
}
public Presenter(MeView view)
{
Binding binding = new Binding("ListItems");
binding.Source = ListItems;
view.MeListView.SetBinding(ListView.ItemsSourceProperty, binding);
// other view events, bindings etc.
}
}
It's not the best way of communicating between View and Model indirectly, but should give you small hint. It will be up to you if you want to split your controls that each will have it's own Presenter or if you want one per window.
I have a class library which contains several user controls. This library uses caliburn micro to bind the UserControls to their corresponding ViewModels using x:Name approach in the same class library.
Now I have an host application which hosts these UserControls in a Window. But this host application doesn't use Caliburn Micro at all. Then how can I initialize caliburn bootstrapper in the library itself.
HostApplication:
<Window>
<ContentControl>
<library:MainUserControl DataContext="{Binding MainUserControlViewModel}"/>
</ContentControl>
</Window>
Class Library
<UserControl x:Class="MainUserControl">
<ContentControl x:Name="OtherUserControlInSameLibrary"/>
</UserControl>
I tried to inherit Bootstrapper base class like AppBootstrapper: Bootstrapperbase and passing false to constructor parameter useApplication. And then I called Initialize() method. But still conventions are not applied.
MainUserControlViewModel Constructor
public MainUserControlViewModel()
{
AppBootstrapper appBootstrapper = new AppBootstrapper();
appBootstrapper.Initialize();
if (Debugger.IsAttached)
LogManager.GetLog = type => new DebugLog(type);
}
BootstrapperBase override
public class AppBootstrapper : BootstrapperBase
{
public AppBootstrapper():base(false)
{
}
}
I also tried to run caliburn debugger but nothing is showed in my output window.
Putting together the mentioned feedback by Caliburn.Micro mantainers and this blog post by Andy Race, it seems somehow it is doable, but the client application code has still to reference CM as well.
Apart from other needed settings (see references), the constraint is due to how the custom control view-model is bound to its view stated in XAML: the only way is to explicitly set the view-model by means of cal:Bind.Model.
...
xmlns:controls="clr-namespace:Control.Library;assembly=Control.Library"
...
<controls:SomeControlView
cal:Bind.Model="Control.Library.SomeControlViewModel" />
For the rest, the client application can totally ignore Caliburn.Micro.
Suppose I have two Pages in WPF namely Page1.xaml and Page2.xaml. I have one viewmodel named PageViewModel.cs. Both of those pages share the same viewmodel.
I can write my code by two methods:
Mehod1:
PageViewModel.cs
public static class PageViewModel
{
}
Page1.xaml
<Window.........>
<Window.DataContext>
<vm:PageViewModel />
</Window.DataContext>
</Window>
Page2.xaml
<Window.........>
<Window.DataContext>
<vm:PageViewModel />
</Window.DataContext>
</Window>
App.xaml
Default xaml code.
Method2:
PageViewModel.cs
public class PageViewModel
{
}
Page1.xaml
<Window DataContext={StaticResource PageViewModel}>
..........
..........
</Window>
Page2.xaml
<Window DataContext={StaticResource PageViewModel}>
..........
..........
</Window>
App.xaml
<vm:PageViewModel x:Key="PageViewModel" />
Can anybody explain the difference between above mentioned two methods?
The primary difference will be that in your first example, any object or method can access your view model, its data, and its methods.
In the second, you have an actual instance (albeit contained in a globally accessible object), so while other objects could still get to it, its not as easy as "access the static (read, global) instance".
Both have the same effect, you get data shared between the two views.
One additional option you may want to consider would be to pass the view model in on the constructor of the view. You have to use the code-behind, but you can give both view's a reference to the same view model object without having any global variables.
If these are subviews, then you could do the following:
MainView.xaml.cs
public void MainView()
{
SubViewModel subVm = new SubViewModel();
//If you are instantiating your views
MySubView view1 = new MySubView(subVm);
MySecondSubView view2 = new MySecondSubView(view2);
//Otherwise
view1.DataContext = subVm;
view2.DataContext = subVm;
}
In the spirit of the locator pattern, you could also simply bind the DataContext property of the sub views to a SubViewModel property on your main View Model.
The one thing to be aware of with this is that the View Model's lifetime will end once both sub views are destroyed. If you need a longer lifetime, then you should use the latter option and point it towards a long-lived object.
In general, I would stay away from static classes. They make unit testing, and good design in general, much more difficult to achieve. If you need a singleton, at least implement one properly as opposed to just using a static class.
This won't answer your question, BradleyDotNET has done that, but I just can't help my self here.
This is a perfect example for using a ViewModelLocator, try to install a framework such as GalaSoft MVVM Light. You may use your Locator to keep track of your viewmodels, static viewmodels are bad pie(you are going to run into alot of problems you can avoid).
I can't see where you have declared your static resource, but I assume it is in App.xaml ?
Check this post for using a viewmodel locator, don't be alarmed by the IOC stuff :). This is really handy and a great way to solve your issue.
Binding to someviewmodel, assuming the vmlocator is defined in App.xaml and that SomeViewModel is present there.
DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=SomeViewModel}"
Hope it helps,
Cheers Stian
Consider that I have an application that just handles Messages and Users I want my Window to have a common Menu and an area where the current View is displayed.
I can only work with either Messages or Users so I cannot work simultaniously with both Views. Therefore I have the following Controls
MessageView.xaml
UserView.xaml
Just to make it a bit easier, both the Message Model and the User Model looks like this:
Name
Description
Now, I have the following three ViewModels:
MainWindowViewModel
UsersViewModel
MessagesViewModel
The UsersViewModel and the MessagesViewModel both just fetch an ObserverableCollection<T> of its regarding Model which is bound in the corresponding View like this:
<DataGrid ItemSource="{Binding ModelCollection}" />
The MainWindowViewModel hooks up two different Commands that have implemented ICommand that looks something like the following:
public class ShowMessagesCommand : ICommand
{
private ViewModelBase ViewModel { get; set; }
public ShowMessagesCommand (ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public void Execute(object parameter)
{
var viewModel = new ProductsViewModel();
ViewModel.PartialViewModel = new MessageView { DataContext = viewModel };
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
And there is another one a like it that will show Users. Now this introduced ViewModelBase which only holds the following:
public UIElement PartialViewModel
{
get { return (UIElement)GetValue(PartialViewModelProperty); }
set { SetValue(PartialViewModelProperty, value); }
}
public static readonly DependencyProperty PartialViewModelProperty =
DependencyProperty.Register("PartialViewModel", typeof(UIElement), typeof(ViewModelBase), new UIPropertyMetadata(null));
This dependency property is used in the MainWindow.xaml to display the User Control dynamicly like this:
<UserControl Content="{Binding PartialViewModel}" />
There are also two buttons on this Window that fires the Commands:
ShowMessagesCommand
ShowUsersCommand
And when these are fired, the UserControl changes because PartialViewModel is a dependency property.
I want to know if this is bad practice? Should I not inject the User Control like this? Is there another "better" alternative that corresponds better with the design pattern? Or is this a nice way of including partial views?
why not use a ContentPresenter/ContentControl with a datatemplate in your mainwindow?
instead of UserControl Content="{Binding PartialViewModel}" />, you can use a:
<ContentPresenter Content="{Binding Path=PartialViewModel}" />
all you have to do: is set your PartialViewmodel to your child viewmodel and create a datatemplate, so wpf will know how to render your childviewmodel
<DataTemplate DataType={x:Type UserViewModel}>
<UserView/>
</DataTemplate>
<DataTemplate DataType={x:Type MessageViewModel}>
<MessageView/>
</DataTemplate>
when ever you set your PartialViewmodel in your MainViewmodel, the right View will render in your ContenControl.
Edit 1
at least you have to implement INotifyPropertyChanged in your ViewModel and fire it when ever the PartViewModel property is set.
Edit 2
if you use Commands in your viewmodels take a look at some mvvm framework implementations like DelegateCommand or RelayCommand. handling ICommand become much easier with this. within your mainviewmodel you can create commands simple like that
private DelegateCommand _showMessageCommand;
public ICommand ShowMessageCommand
{
get
{
return this._showMessageCommand ?? (this._showMessageCommand = new DelegateCommand(this.ShowMessageExecute, this.CanShowMessageExecute));
}
}
This isn't a bad approach at first sight, it might be just fine to use in a small app.
However, there are a couple of things that aren't that nice:
ViewModelBase needs to be a DependencyObject to have a DependencyProperty. In the real world I 've found that it's very annoying to have to treat ViewModels in a single-threaded manner (there are lots of async operations one might want to perform).
It doesn't scale; changing the layout will require significant amounts of work.
Any decent MVVM framework makes UI composition easy by providing infrastructure to compose sub-Views into your main View. In Prism (which is my personal preference), this happens with Regions.
I would look at using an MVVM framework such as Caliburn.Micro which makes view composition incredibly easy. If you have a property on your view model which is a view model type, and a ContentControl on your view which is named the same as your property, then Caliburn.Micro will locate that view models corresponding view via conventions, do the binding for you automatically, and inject the view into the ContentControl.
I would also avoid using dependency properties on your view models, and instead implement INotifyPropertyChanged. Caliburn.Micro comes with a PropertyChangedBase type which implements this interface, and also provides a helper method for invoking the PropertyChanged event using lambda expressions rather than magic strings (which is much better for refactoring later).
EDIT
http://msdn.microsoft.com/en-us/library/ms743695.aspx shows an example of implementing INotifyPropertyChanged.
To achieve what you want to do in Caliburn.Micro, you would do something like the following (a crude example, but it shows you how easy it is doing view composition using an MVVM framework):
public class MainViewModel : Conductor<IScreen>.Collection.OneActive
{
private UsersViewModel usersViewModel;
private MessagesViewModel messagesViewModel;
public UsersViewModel UsersViewModel
{
get { return this.usersViewModel; }
set { this.usersViewModel = value; this.NotifyOfPropertyChanged(() => this.UsersViewModel);
}
public MessagesViewModel MessagesViewModel
{
get { return this.messagesViewModel; }
set { this.messagesViewModel = value; this.NotifyOfPropertyChanged(() => this.MessagesViewModel);
}
public MainViewModel()
{
this.UsersViewModel = new UsersViewModel();
this.MessagesViewModel = new MessagesViewModel();
this.Items.Add(this.UsersViewModel);
this.Items.Add(this.MessagesViewModel);
// set default view
this.ActivateItem(this.UsersViewModel);
}
public ShowUsers()
{
this.ActivateItem(this.UsersViewModel);
}
public ShowMessages()
{
this.ActivateItem(this.MessagesViewModel);
}
}
Note that UsersViewModel and MessagesViewModel would derive from Screen.
To invoke the ShowUsers or ShowMessages verbs with Caliburn.Micro, you just need to create view controls with the same name. The conductor type has an ActiveItem property which is the currently conducted item, so you can add a ContentControl to your MainView.xaml which is named ActiveItem, and Caliburn.Micro will take care of injecting the correct view.
So your MainView.xaml may look like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinition>
<!-- Menu in left hand column -->
<StackPanel Grid.Column="0">
<Button x:Name="ShowUsers">Show Users</Button>
<Button x:Name="ShowMessages">Show Messages</Button>
</StackPanel>
<!-- Currently active item -->
<ContentControl x:Name="ActiveItem" Grid.Column="1" />
</Grid>
you should take a look at prism. It gives you region handling.
I would also take a look at MEF to Export Views and on this way maintain an extensibility for your project.
My WPF project will be organised like this :
Screens
Group1
Screen1
View.xaml
ViewModel.cs
Group2
Screen2
View.xaml
ViewModel.cs
To show the Screen1 from the Screen2 I'll use something like this: ScreenManager.Show("Group1.Screen1") This looks (using reflection) in the Screens.Group1.Screen1 namespace for a View and a ViewModel and instantiates them.
How can I eliminate the magic string without coupling Screen1 and Screen2 (I don't want the classes in Screen2 to use the Screen1 namespace). Also I would like some kind of screen discovery (autocompletion/intellisense)
Or maybe some way (automate test) to verify that all calls to ScreenManager.Show are valid.
Update :
I came up with this:
public class ScreenNames
{
public Group1Screens Group1;
public class Group1Screens
{
public ScreenName Screen1;
}
}
public sealed class ScreenName
{
private ScreenName() { }
}
public class ScreenManager : IScreenManager
{
public void Show(Expression<Func<ScreenNames, ScreenName>> x) {}
}
Usage:
screenManager.Show(x=>x.Group1.Screen1);
Not ideal but I suppose violating DRY is still better than magic strings. And I can automatically test (with reflection) that all calls are valid.
You don't need all that ScreenManager stuff in WPF, because the DataTemplate engine can take care of this for you with pure markup.
You can simply databind a particular area of your application with a ContentPresenter and a bunch of DataTemplates. Bind the area to a property of a 'root' ViewModel, and let the 'root' ViewModel implement INotifyPropertyChanged so that WPF knows if you change the ViewModel in that area.
public class RootViewModel : INotifyPropertyChanged
{
public object Screen1ViewModel { get; }
public object Screen2ViewModel { get; }
}
Databind one ContentPresenter control to the Screen1ViewModel property using
<ContentControl Content="{Binding Path=Screen1ViewModel}" />
and similarly for the next one. When you need to change the content of Screen1, you simply re-assign Screen1ViewModel from code, and because of the raised PropertyChanged event, WPF will pick it up and bind the new ViewModel to a new View.
The DataTemplates may be as simple as this:
<Window.Resources>
<DataTemplate DataType="{x:Type foo:MyViewModel}">
<self:MyControl />
</DataTemplate>
<DataTemplate DataType="{x:Type foo:MyOtherViewModel}">
<self:MyOtherControl />
</DataTemplate>
</Window.Resources>
In case you are not familiar with it, this article on MVVM in WPF is an excellent introduction.
Finally I used T4 code generation to generate my ScreenNames class. I did that by adapting this code : Auto generate strong typed navigation class for all user controls in ASP.NET web application