Why is the Frame property null on a pivot item?
I have a summary page defined like this:
<Grid x:Name="LayoutRoot" Background="Transparent" >
<Pivot Title="JOETZ">
<PivotItem Header="kampen" Name="PvOne">
<views:CampsPage/>
</PivotItem>
<PivotItem Header="kalender" Name="PvTwo">
<views:CalendarPage/>
</PivotItem>
<PivotItem Header="profiel" Name="PvThree">
<views:LoginPage/>
</PivotItem>
</Pivot>
</Grid>
Let's take the first page ( CampsPage
) as an example : it contains ListBox
an overview of the camps. when the item is clicked, it should go to the detail view. To navigate, I thought this should be enough:
Frame.Navigate(typeof(CampDetail), selectedCamp);
but this calls a NullReferenceException
, because Frame
apparently null
.
On the other hand, it does navigation like this:
var frame = Window.Current.Content as Frame;
if (frame == null) { return; }
frame.Navigate(typeof(CampDetail), selectedCamp);
And this approach:
var frame = ((App) Application.Current).RootFrame;
frame.Navigate(typeof(CampDetail), selectedCamp);
Why is the property Frame
not set in the pivot position? And as a follow up: how do I navigate out of the pivots?
source to share
You seem to have already found most of the information you need. I can confirm that this is the way to navigate (as you yourself suggested):
var frame = (Frame)Window.Current.Content;
frame.Navigate(typeof(CampDetail), selectedCamp);
Obtaining Frame
in this way is fairly common and safe. There Window.Current.Content
will be nothing in it except Frame
if you have not placed it. In this case, it simply won't Frame
, and you still won't be able to navigate that way (ie, you have to manually change the property Window.Current.Content
to show different content, and manually manipulate the Back button).
Some ideas / suggestions
I would suggest creating a static class with a static method Navigate
that does the job (gets the Frame and calls its method Navigate
).
It might be even more convenient to (also) make an extension method for the UserControl class that does navigation. This way you just call this.Navigate(typeof(CampDetail), selectedCamp)
on all UserControls.
I also suggest being consistent and always using this method and never using the property Page.Frame
(unless you really need to, for some reason). This will make it easier to find / troubleshoot navigation related problems as every navigation will go through this method.
You have to use UserControl instead of Page
A Page
is part of the application that should be displayed in full screen. It is not considered part of another page. The way you use it, it works like a UserControl (which is what you should be using in this case). Page
-specific functions will only work if you navigate to it using an object Frame
.
Custom controls and templated controls, on the other hand, are reusable parts of pages. So, if you need to have some user interface (with its functionality) on multiple pages - you make one of these controls.
So, I suggest you change <views:CampsPage/>
, <views:CalendarPage/>
and <views:LoginPage/>
to UserControls instead of Pages. If you need pages (for navigation purposes), create pages that simply display these UserControls.
Back button
You don't seem to need this anymore, but I'll put a few lines in anyway.
Default "Back" button simply indicates close the application. You can manually handle the Back button and perform some non-default actions using an event Frame
in Window.Current.Content
on GoBack()
, if possibleHardwareButtons.BackPressed
.
Edit
I was wrong about the default button action and fixed it. I'm pretty sure it automatically moved back through the stack.
source to share
I'm not sure if this is the best answer (I'm sure it isn't :), but it should work.
The problem is what CampsPage
is, as I suspect, the class of the page inside a different container than the Frame. You didn't go to this page, it's not inside Window.Current.Content
- inside Pivot. Thus, the Frame property has a value null
.
Here's my ugly approach:
Extend your page class with DependencyProperty - links to the parent page:
public class ExtendedPage : Page
{
public Page ParentPage
{
get { return (Page)GetValue(ParentPageProperty); }
set { SetValue(ParentPageProperty, value); }
}
public static readonly DependencyProperty ParentPageProperty =
DependencyProperty.Register("ParentPage", typeof(Page), typeof(Page), new PropertyMetadata(0));
}
Then make your CampsPage
(and another) this ExtendedPage type. Then bind the ParentPage property to the current page inside the frame:
<Page Name="MyMainPage" ... rest of things ...>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Pivot Title="JOETZ">
<PivotItem Header="kampen" Name="PvOne">
<local:CampsPage ParentPage="{Binding ElementName=MyMainPage}"/>
</PivotItem>
... rest of the code
Then you can navigate CampsPage
like this:
ParentPage.Frame.Navigate(typeof(DetailPage));
As I said, there are probably beter solutions, but if not otherwise, it might be helpful.
Example: can be downloaded from GitHub .
source to share