Reputation: 1105
I'm trying to create a memory game while strictly following the MVVM pattern to learn how to use it. Now I have a problem with creating views at run time.
I've created the following project structure:
The dependencies are as follows: StartApplication project -> View project -> ViewModel project -> Model project
After clicking a button on the MainWindowView the ICommand function for that button within the MainWindowViewModel will load a MemoryCardModel30 instance from the Model project. For each Card within the MemoryCardModel30 instance a CardViewModel will be created.
Now to the problems I face: How to create the CardView instances, how to link their DataContexts to the CardViewModels and how to arrange/assign the CardViews on the MainWindowView? The ViewModel project can't create Views as it has no dependency to the View project (would create a circular dependency and breaks the pattern). How to solve this issue while following the MVVM pattern?
P.S.: The CardViews need to be positioned exactly by x and y pos. which will require some complicated calculations which should go tho the corresponding CardViewModel. So some basic layouts like grid will not be sufficient I think.
Upvotes: 2
Views: 3832
Reputation: 37059
Display them in an ItemsControl
. I'm assuming that MainWindowViewModel.Cards
is ObservableCollection<CardViewModel>
.
<ItemsControl
ItemsSource="{Binding Cards}"
>
<!--
This creates UI for each item. There are other ways, if you've got a collection
of heterogeneous item types.
-->
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:CardViewModel">
<views:CardView />
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--
Make it use a Canvas to be the actual container for the items, so we can control
their position arbitrarily, instead of the default StackPanel that just stacks
them up vertically.
-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--
The ItemsControl will put the instantiated item templates in ContentPresenters
that it creates. The positioning attributes have to go on the ContentPresenters,
because those are the direct children of the Canvas. The ContentPresenters are
the "item containers". You can customize them via the ItemContainerStyle property
of the ItemsControl.
-->
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<!--
The datacontext will be CardViewModel.
Bind Canvas.Left and Canvas.Top to appropriate properties
of CardViewModel. I'll assume it's got Point Position { get; }
A much better, more "pure MVVM" way to do this is for the items to
provide some kind of abstraction, maybe row/column or something else,
and either place them in a Grid or UniformGrid or some other kind of
dynamic layout control, or else convert that abstraction into Canvas
coordinates with a value converter on the Binding.
Then you can display the same item objects in different ways at the same
time without locking them into one layout.
Don't drive yourself crazy striving for ideological purity at the expense
of getting code out the door, but do consider redesigning that part.
-->
<Setter Property="Canvas.Left" Value="{Binding Position.X}" />
<Setter Property="Canvas.Top" Value="{Binding Position.Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
This is the canonical Way to Do It in WPF/MVVM. Use DataTemplate
s to create view instances of the appropriate type. The viewmodel is in charge of what objects should be presented to the user; the views are responsible for how they're shown. You don't need or want any MVVM framework for this. The built-in DataTemplate
features of WPF are enormously powerful. Don't trust anybody who thinks you need anything else for a project within two orders of magnitude of this size.
Upvotes: 5
Reputation: 16991
I think I misunderstood your question. I originally thought you were asking how to display a new window for specific view models. While this answer won't specifically apply to you, I'll leave it up, as it is tangentially related. It may help others confused about what to search for.
I have a ViewManager
class that links view types to viewmodel types. One of the methods on it is ShowViewFor
that handles this task, it takes a viewmodel instance and:
DataContext
of that view instance to the viewmodel that was passed in.It also does a bunch of other tasks like tracking open views, displaying message boxes and dialogs, etc.
The ViewManager
is available though an IOC container via an interface, so it can be mocked up for unit tests.
I'm sure there are many existing frameworks out there that do this, but like you, I wanted to learn MVVM from "the roots up".
Upvotes: 0