Enabling partial views when using the Mode-View-ViewModel design pattern

Keep in mind that I have an application that simply processes Messages and Users . I want my window to have a common Menu and the area where the current View displayed.

I can only work with messages or users, so I can not work simultaneously with both views. Therefore, I have the following controls

  • MessageView.xaml
  • Userview.xaml

Just to make it a little easier, both Message Model and User Model look like this:

  • Name
  • Description

Now I have the following three types of Models:

  • MainWindowViewModel
  • UsersViewModel
  • MessagesViewModel

UsersViewModel and MessagesViewModel both simply retrieve the ObserverableCollection<T> it relative to the Model , which is linked in the corresponding View as follows:

<DataGrid ItemSource="{Binding ModelCollection}" />

MainWindowViewModel connects two different Commands that have implemented ICommand , which looks something like this:

 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 that will show users. Now it has introduced ViewModelBase , which only has 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 MainWindow.xaml to dynamically display User Control as follows:

<UserControl Content="{Binding PartialViewModel}" />

There are two buttons in this Window that launch commands:

  • ShowMessagesCommand
  • ShowUsersCommand

And when they start, UserControl changes because PartialViewModel is a dependency property.

I want to know if this is bad practice? Should I not introduce a user control this way? Is there another “better” alternative that better matches the design pattern? Or is this a good way to include partial views?

+6
c # design-patterns wpf mvvm
source share
4 answers

why not use ContentPresenter / ContentControl with a datatemplate in your main window?

instead of UserControl Content = "{Binding PartialViewModel}" / ">, you can use:

  <ContentPresenter Content="{Binding Path=PartialViewModel}" /> 

all you have to do is install PartialViewmodel into your view model and create a data table so wpf will know how to display your childviewmodel

 <DataTemplate DataType={x:Type UserViewModel}> <UserView/> </DataTemplate> <DataTemplate DataType={x:Type MessageViewModel}> <MessageView/> </DataTemplate> 

when you set the PartialViewmodel in your MainViewmodel, the right view will be displayed in the ContenControl.

Change 1 at least you need to implement INotifyPropertyChanged in your ViewModel and run it when the PartViewModel property is set.

Change 2 if you use Commands in your view models, look at some mvvm implementation implementations like DelegateCommand or RelayCommand. ICommand handling has become much easier with this. within your main model, you can create simple commands like this

 private DelegateCommand _showMessageCommand; public ICommand ShowMessageCommand { get { return this._showMessageCommand ?? (this._showMessageCommand = new DelegateCommand(this.ShowMessageExecute, this.CanShowMessageExecute)); } } 
+2
source share

This is not a bad approach at a glance; it can be just fine to use in a small application.

However, there are a few things that are not so good:

  • ViewModelBase must be DependencyObject in order to have DependencyProperty . In the real world, I found it very annoying to use ViewModels in single-threaded mode (there are many asynchronous operations that can be performed).
  • It does not scale; changing the layout will require significant costs.

Any decent MVVM environment makes it easy to build a user interface by providing the infrastructure to create subviews in your main view. In Prism (which is my personal preference) this happens with Regions .

+2
source share

I would look at using an MVVM environment like Caliburn.Micro , which makes viewing a song incredibly easy. If you have a property in your view model, which is a type of view model, and a ContentControl in your view, which is called the same as your property, then Caliburn.Micro will find this view that matches the view through the legend, do the binding for you automatically , and enter the view in the ContentControl .

I would also avoid using dependency properties on your view models and use INotifyPropertyChanged instead . Caliburn.Micro comes with a PropertyChangedBase type that implements this interface, and also provides a helper method for raising 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 implementation of INotifyPropertyChanged.

To achieve what you want to do in Caliburn.Micro, you would do something like the following (a rough example, but it shows you how easy it is to compose a view using the MVVM environment):

 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 will be output from Screen .

To invoke ShowUsers or ShowMessages with Caliburn.Micro, you just need to create view controls with the same name. The wire type has the ActiveItem property, which is the current element, so you can add ContentControl to your MainView.xaml called ActiveItem , and Caliburn.Micro will take care to enter the correct view.

So your MainView.xaml might look like this:

 <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> 
+1
source share

you should take a look at the prism . It gives you regional management. I would also like to take a look at MEF for exporting species and thus support the extensibility of your project.

0
source share

All Articles