Example of DataTemplate-based page navigation for WPF.
- Define
DataTemplate
s to define a mapping from view models to views (control) in resource dictionary - Show views by setting view models to content of
ContentControl
(referred to as frame) - Changing content of the frame is navigation, which is done in the view model layer
Note that you don't need System.Windows.Controls.Frame
.
Let Foo
be a class of data context for a view FooControl
.
If you define a DataTemplate
for Foo
like this, the template is automatically applied whenever WPF would display the Foo
instance.
For example, you have the following in a resource dictionary:
<DataTemplate DataType="{x:Type foo:Foo}">
<foo:FooControl />
</DataTemplate>
and put a control on window to display an instance of Foo
:
<ContentControl Control="{Binding Foo}" />
then you will see FooControl
with DataContext bound to the Foo
property.
See AppResourceDictionary.xaml
for real implementation.
Pages in frame (a control to host pages) must dispatch some message to the parent frame to go to another page. The cyclic reference, "the frame know pages and the pages also know the frame", is bad for maintainability as separation of concerns principle.
You can de-couple the frame and pages using RelativeSource
. See the following example:
<UserControl>
<!-- Inside FooControl (page) -->
<Button Content="Go some page"
Command="{Binding
DataContext.NavigateCommand,
RelativeSource={RelativeSource
AncestorType={x:Type layoutFrames:LayoutFrameControl}
}"
CommandParameter="{Binding NavigateRequest}" />
</UserControl>
Assume that the user control is a page of LayoutFarmeControl
(with DataContext LayoutFrame
). The RelativeSource
binding finds a lowest ancestor of the type specified with AncestorType
, i.e. LayoutFrameControl
, and use it as binding source. Hence The value of the binding will be LayoutFrameControol.DataContext.NavigateCommand
= LayoutFrame.NavigateCommand
. That's how you can use NavigateCommand
inside pages, without using reference to the frame.
The command parameter NavigateRequest
is something to specify where to go, which also doesn't need to know the frame.
I'm not a big fun of typical xxxViewModel
suffix for view models. In this repository the view models have no suffixes, but views have -Control suffix (except for windows). E.g. LayoutFrame
is a view model for corresponding view LayoutFrameControl
.
Requests are objects intended to be a parameter of commands. I know the word is NOT the best choice but at least makes sence.
This system can be exetended to other kind of navigation such as open/close drawer. It should be future work.