Including partial views when applying the Mode-View-ViewModel design pattern - c#

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.

Related

Should I display data using MVP and WPF through binding?

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.

Correct design for binding data to a XAML control in a view, from the ViewModel

For context, I am building a universal Windows Store app.
I'm just starting to learn C# and MVVM patterns and I need help correctly implementing binding.
I have followed this tutorial (Binding) and understand how it works, however in this example the code which does the binding is stored within the View Class.
public partial class MainPage : Page
{
public ObservableCollection<TermTest> MyTerms = new ObservableCollection<TermTest>();
public MainPage()
{
this.InitializeComponent();
MyTerms.Add(new TermTest("BNC", "Wire"));
MyTerms.Add(new TermTest("Lens", "Collects light"));
this.DataContext = new CollectionViewSource { Source = MyTerms };
}
As I understand it however this is poor design. In my implementation I will be using my Model to retrieve data which will get put into an Observable Collection. Then in my ViewModel I will want to bind the ObservableCollection to the XAML controls in which it is being used, not send the Collection to the View and then call a method in the View to populate the XAML controls.
Is that the correct way of doing this and, if so, how should it be done because I do not know how to expose the XAML controls to my ViewModel (and don't think I should be, right?).
I know I can expose the control creating a new instance of Mainpage but that is useless as I would need to bind to the current instance.
Mainpage Test = new MainPage();
Can someone please help me explain this - I have been through a lot reading and either not found the answer or not understood it!
Thanks, James
To begin, you definitely have the right idea.
What you want to do is create a ViewModel object (have it implement INotifyPropertyChanged) something like:
public class MainViewModel : INotifyPropertyChanged
{
//INPC implementation
public ObservableCollection<TermTest> MyTerms
{
//Standard INPC property stuff
}
}
Note that I used a property. You can only bind to properties, and you'll need the set method to raise the PropertyChanged event.
Then, you set the data context for the view. You can do this a number of ways, but the simplest is to do this:
public MainView() //Default Constructor
{
InitializeComponent();
DataContext = new MainViewModel();
}
Finally, bind!
<ListView ItemsSource="{Binding MyTerms}"/>
Also, if you don't want to touch the code behind of your window, you can do something like this:
<Window.Resources>
<YourNamespace:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainViewModel}">
<ListView x:Name="TermsListView" ItemsSource="{Binding MyTerms}">
</ListView>
</Grid>
If you want understand in details this pattern I recommend you read this article:WPF MVVM step by step (Basics to Advance Level)

WPF Caliburn.Micro/mvvm Navigation

I'm building a project, and one of the biggest problems I've come across until now is navigation.
I've been looking for some time now for examples of caliburn.micro/mvvm navigation, but they all seem to be really long and I couldn't really understand much of it (beginner here!).
Some info about my project:
I want there to be an outer window/shell, with menu links/tabs that open pages according to the button clicked inside an inner part of the shell, and be able to open change the page from within a one.
I currently have: ShellViewModel.cs, MainViewModel.cs, my models, and my views.
For now, all I need to know is how to make MainViewModel load inside shellviewmodel on startup(using contentcontrol/frames...), and how to move from one page to another.
You could also just write it in points, and link me to some useful examples, and I believe I could continue from there. It'd be best to get a thorough explanation of stuff if possible.
Have a read about Conductors and Screens on the official documentation.
As a simple example, your ShellViewModel could be a Conductor of one active screen (i.e. only one screen becomes active/inactive at a time):
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
You can then set the ActiveItem of the Conductor to the view model instance that you wish to be currently active:
this.ActivateItem(myMainViewModel);
A collection Conductor type also provides an Items collection which you can populate as you instantiate new windows. Viewmodels in this Items collection may be those that are currently deactivated but not yet closed, and you can activate them by using ActivateItem as above. It also makes it very easy to create a menu of open windows by using an ItemsControl with x:Name="Items" in your ShellView.
Then, to create the ShellView, you can use a ContentControl and set its name to be the same as the ActiveItem property, and Caliburn.Micro will do the rest:
<ContentControl x:Name="ActiveItem" />
You can then respond to activation/deactivation in your MainViewModel by overriding OnActivate/OnDeactivate in that class.
In ShellView you use a content control like this:
<ShellView xmlns:cal="http://caliburnproject.org/">
<StackPanel>
<Button Content="Show other view" cal:Message.Attach="ShowOtherView" />
<ContentControl cal:View.Model="{Binding Child}" />
</StackPanel>
</ShellView>
ShellViewModel:
public class ShellViewModel : Screen
{
private object Child;
public object Child
{
get{ return child; }
set
{
if(child == value)
return;
child = value;
NotifyOfPropertyChange(() => Child);
}
}
public ShellViewModel()
{
this.Child = new MainViewModel();
}
public void ShowOtherView()
{
this.Child = new FooViewModel();
}
}
So this is a very basic example. But as you see, your ShellView provides a ContentControl, which shows the child view. This ContentControl is bound via View.Model to the Child property from your ShellViewModel.
In ShellView, I used a button to show a different view, but you can also use a menu or something like that.

How do I nest multiple ViewModels in a top level ViewModel, in a sort of hierarchy that can be dispersed throughout my Views?

Right now I have a rather large ViewModel called MainViewModel, that contains a lot of commands and properties that could be organized into more reasonably named ViewModels.
I however, only have one window, and so need to be able to cherry-pick the valid data and Commands from each of the SubViewModels....
I figure this could be achieved using properties, but I'm not so sure how to approach this in the XAML. (Or if there is an implication in the ViewModels themselves)
I realize that I can set the DataContext on SubViews as I see fit, but I want to avoid having my View dictate the hierarchy/organization of my ViewModels.
Ex. pseudo code
SubAViewModel mvmB = new SubBViewModel();
SubAViewModel mvmA = new SubAViewModel();
MainViewModel mvm = new MainViewModel( mvmA, mvmB );
<Window DataContext="{StaticResource MainViewModel}">
//This is clearly wrong but is sort of what I am trying to achieve
<MenuItem Command="{Binding Path=MainViewModel.SubAVM.TargetCmd}" />
It's entirely possible that a MenuItem or some other UserControl would want to access a Command in SubBViewModel and a property in SubAViewModel.
If you set the data context of the View to the MainViewModel and you expose the SubAVM as a property like this one:
public SubViewModel SubAVM {
get {
return subAVM;
}
set{
if (subAVM == value)
{
return;
}
subAVM = value; //implement the OnPropertyChanged ...
}
}
Of course the Path on a MenuItem would be SubAVM.TargetCmd as by the dependency hierarchy the main path is already MainViewModel for a Menu.

Decouple the screens without magic strings

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

Categories