6

I want to make a layout like the one used in any website - the header, sidebar and footer stay the same but the center part. I have multiple pages/windows to show in a wpf blend C# application and they are totally different. For example, stackoverflow has a layout for the homepage and another one for each Question. Here's another exemple:

first  page second page

I had to do that in a previous project and I used a single grid layout and then, for each page, I had to hide() all of them and show that each one on top -

What's the trick? How can I do the same thing in a wpf application? In a typical C# application I would have to open a child window each time but that seems ugly these days.

Thank you in advance!

ioan
  • 751
  • 1
  • 9
  • 26
  • 2
    You can create one main window and host usercontrols depends on your requirement. – sobby01 Jul 24 '14 at 11:51
  • 1
    Also you have another option that I use to do things like that. In your MainWindow create a Layout that you want. Keep Frame control whereever you want a change. And show pages depending on user actions. – Vishal Jul 24 '14 at 11:57

3 Answers3

8

If you are going to use Pages in WPF, then you will need to read the Navigation Overview page on MSDN. In short however, you can navigate between Pages in a WPF Application by using the NavigationService Class. To change the page from code behind, you could do something like this:

NextPage page = new NextPage();
NavigationService.Navigate(page);

To let the users change the Page, you can use the Hyperlink Class in your Pages:

<Hyperlink NavigateUri="pack://application:,,,/AppName;component/Pages/NextPage.xaml">
    Navigate to Next Page
</Hyperlink>

To get your desired page setup, you will have to load your Pages into a Frame, which you can then layout wherever you like in MainWindow.xaml:

<Frame Source="pack://application:,,,/AppName;component/Pages/SomePage.xaml" />
naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • The `Navigation Overview` MSDN page for .NET Framework 4.5 seems to be removed/broken, but you can still find the [.NET Framework 4.0 version here](https://msdn.microsoft.com/en-us/library/ms750478%28v=vs.100%29.aspx). – Marcus Mangelsdorf Sep 15 '15 at 20:09
  • Many thanks @Merlin2001... I've updated the link now. – Sheridan Sep 16 '15 at 10:46
4

Sounds like you need a custom usercontrol and some databinding.

You can declare DataTemplates in XAML as resources with the model type as key, so that WPF chooses the correct DataTemplate automatically:

  • Have a main ViewModel, which exposes a ImageSourceViewModel property. This property would either return a CameraSourceViewModel or a FileSourceViewModel, as appropriate.

  • In your page, the DataContext would be the main ViewModel, and you'd have XAML like this:

Then,

<Page x:Class="Page1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:my="clr-namespace:WpfApplication1"
  mc:Ignorable="d" 
  d:DesignHeight="300" d:DesignWidth="300"
  Title="Page1">
<Page.Resources>
    <DataTemplate DataType="{x:Type my:CameraSourceViewModel}">
        <my:CameraSourceView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type my:FileSourceViewModel}">
        <my:FileSourceView/>
    </DataTemplate>
</Page.Resources>
<Grid>
    <ContentControl Content="{Binding ImageSourceViewModel}"/>
</Grid>

I should point out that this example uses the MVVM pattern to allow the viewmodel layer to decide on the content in the middle. Hopefully this is clear enough, if not, give me a shout and I'll try to expand it!

Immortal Blue
  • 1,691
  • 13
  • 27
2

Let's say I have main view model where I've created a CurrentPage property that will tell which page you want to display.

/// <summary>
/// Returns the page ViewModel that the user is currently viewing.
/// </summary>
public ViewModelBase CurrentPage
{
    get { return _currentPage; }
    private set
    {
        if (value != _currentPage)
        {
            if (_currentPage != null)
                _currentPage.IsCurrentPage = false;

            _currentPage = value;

            if (_currentPage != null)
                _currentPage.IsCurrentPage = true;
            RaisePropertyChanged(() => CurrentPage);
        }
    }
}

And in your xaml you can bind your page under some control. Let's say I am doing it inside a Border element.

<!-- CURRENT PAGE AREA -->
<Border Background="White" Grid.Column="1" Grid.Row="0">
    <HeaderedContentControl Content="{Binding Path=CurrentPage}" 
        Header="{Binding Path=CurrentPage.DisplayName}" />
</Border>

You can define view to your view model in resources just like this:
(partially complete XAML)

<UserControl x:Class="BAT.View.BATWizardView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:view="clr-namespace:BAT.View"
        xmlns:viewmodel="clr-namespace:BAT.ViewModel"
        mc:Ignorable="d" 
        d:DesignHeight="350" d:DesignWidth="600">

   <UserControl.Resources>
      <!-- These four templates map a ViewModel to a View. -->
      <DataTemplate DataType="{x:Type viewmodel:MyComparisonViewModel1}">
          <view:MyView1 />
      </DataTemplate>

      <DataTemplate DataType="{x:Type viewmodel:MyComparisonViewModel2}">
          <view:MyView2 />
      </DataTemplate>

   </UserControl.Resources>
   <Grid>
      <Border Background="White" Grid.Column="1" Grid.Row="0">
         <HeaderedContentControl Content="{Binding Path=CurrentPage}" 
                      Header="{Binding Path=CurrentPage.DisplayName}" />
      </Border>
   </Grid>
</UserControl>

See if that helps.

Kapol
  • 6,383
  • 3
  • 21
  • 46
sobby01
  • 1,916
  • 1
  • 13
  • 22