As promised, here's a post explaining the current problem I'm working on with WPF, and my hacked-together solution. I hope you guys find it useful, and if there are possible improvements, just let me know.
Problem
I want my application to be contained in a single window. The user should be able to navigate through the application just like he would navigate through a website. Modal dialogs are OK. I'm placing this restriction because in the future the application may be ported to a tablet running Windows 7.
Mock-up
As you can see, we have two main regions: a navigation bar and a display region. There is also a list of tabs (these may end up being just menu items or buttons later). When you click on a tab, the navigation bar changes to show navigation entries for that tab, and the display region changes to display the selected page from the navigation bar. By default, the first page is displayed.
Whenever you click a button in the navigation bar, the display region changes to display the associated page. The page contains display elements and editable controls.
Solution
xterm pointed me at the wonderful
MVVM Light Framework. So that's what I've been using to create my views (which roughly correspond to "pages" in the application, but not necessarily).
The
MVVM Pattern allows us to separate UI logic from application logic in a very clean way. MVVM Light provides a Messenger facility that allows parts of the application to send and register for specific types of messages. This allows communication between separate parts without them necessarily being aware of each other.
WPF provides two very important components: the Frame, and the NavigationService. A Frame is just a container for content that can be navigated (WPF Pages), and the NavigationService is what you use to move through your content. Every frame has a NavigationService (and a Journal, but that's not relevant here).
MainWindow.xaml:
<Frame x:Name="navBar"
DockPanel.Dock="Left"
Source="BeginBar.xaml"
NavigationUIVisibility="Hidden"
Width="200">
</Frame>
<Frame x:Name="dispRegion"
Source="Search.xaml"
NavigationUIVisibility="Hidden">
</Frame>
I hide the "Forward" and "Back" buttons by setting the NavigationUIVisibility property. Now I need to make these two frames receptive to Navigation messages.
MainWindow.xaml.cs:
Messenger.Default.Register<Uri>(this, "NavigationRequest",
(uri) => dispRegion.NavigationService.Navigate(uri));
Messenger.Default.Register<Uri>(this, "NavBarRequest",
(uri) => navBar.NavigationService.Navigate(uri));
I've registered each region in my window to a specific type of message that will cause it to navigate to a different page. Each message carries a Uri as a parameter.
Now I have to bind one of the tab controls to a command that will cause a navigation message to be sent to the MainWindow, which will subsequently Navigate one of the frames, based on the message.
MainViewModel.cs:
public RelayCommand goToProfile { get; private set; }
public MainViewModel()
{
goToProfile = new RelayCommand(
() => Messenger.Default.Send<Uri>(new Uri("Profile.xaml", UriKind.Relative),
"NavigationRequest"));
}
This command uses the MVVM Light Messenger to broadcast a NavigationRequest message with the Uri of "Profile.xaml". The subscribers to this message should then navigate to that Page. I still need to bind this command to a control, so that the control triggers it.
MainWindow.xaml
<MenuItem Header="Patient" Command="{Binding goToProfile}" />
And there you have it. I create commands for each control that I want to use for navigation, and wire them up through the Messenger service. Whenever I click a control, it will cause one of the frames to navigate to a certain page. This lets me contain my application in one window instead of opening new windows all the time.
I figured most of this out by scouring the internet and trying various examples, so this is really a stitching together of lots of code you will find online. Let me know if this is a good (or bad) solution!