Home > Mobile >  Why does Page disappears when I move it between frames?
Why does Page disappears when I move it between frames?

Time:11-20

I have a window with a Main frame which contains a Page. And I have another Page that contains the first Page. And I want to move between this two Pages. Here is the first page: enter image description here
And the second one: enter image description here
Everything is ok for the first time when I run the app. But when I come back to the first Page and then to the second, the first Page in the second Page's frame disappears: enter image description here
Here is the MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public static TestPage testPage = new TestPage();
    public static CombinedPage combinedPage = new CombinedPage();
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Butt1_Click(object sender, RoutedEventArgs e)
    {
        combinedPage.TimerFrame.Content = null;
        Main.Content = testPage;
    }

    private void Butt2_Click(object sender, RoutedEventArgs e)
    {
        Main.Content = null;
        combinedPage.Call();
        Main.Content = combinedPage;
    }
}

MainWindow.xaml:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="80 px"></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="70 px" ></ColumnDefinition>
        <ColumnDefinition Width="*" ></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Vertical" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.RowSpan="2">
        <Button x:Name="Butt1" Height="50" Width="50" Click="Butt1_Click"  />
        <Button x:Name="Butt2" Height="50" Width="50" Click="Butt2_Click"  />
    </StackPanel>
    <Frame NavigationUIVisibility="Hidden" x:Name="Main" Grid.Row="1" Grid.Column="1"/>
</Grid>

CombinedPage.xaml.cs:

public partial class CombinedPage : Page
{
    public CombinedPage()
    {
        InitializeComponent();
    }

    public void Call()
    {
        TimerFrame.Content = MainWindow.testPage;
    }
}

CombinedPage.xaml:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <GridSplitter Grid.Column="1" Grid.RowSpan="3" Width="5" Background="Black"
                VerticalAlignment="Stretch" HorizontalAlignment="Center" />
    <GridSplitter Grid.Row="1" Grid.ColumnSpan="3" Height="5" Background="Black"
                VerticalAlignment="Center" HorizontalAlignment="Stretch" />

    <Frame NavigationUIVisibility="Hidden" x:Name="TimerFrame" Grid.Row="0" Grid.Column="0"/>
</Grid>

So the final question is - Why does the first Page disappear when I move between pages?

CodePudding user response:

I'm actuall using the Frame quiet rarily. It's a very heavy control that is only recommended when you want to display web content.
I highly recommend to use a ContentControl and a DataTemplate for each page/view.
See this example: enter image description here (Source: Microsoft Docs: Navigation Overview)

As you can learn from the diagramm, setting the Frame.Source or Frame.Content results in a series of events that ends with the LoadCompleted event.
You are currently navigating to pages too early. The testPage is still loaded in the recent Frame and is therefore not able to be displayed in the next Frame.

The special problem with your scenario is that you want to load the same Page instance in different Frame instances. Since the Page instance can only be rendered once, you must unload the Page instance from the recent Frame in order to show it in the next Frame.

To fix your problem you have two options:

  1. Handle the LoadCompleted event after the null assignment/Frame reset and set the new Page from the handler.

  2. Use the Dispatcher with a priority of at least DispatcherPriority.Background to defer the assignment after the null assignment/Frame reset. For example:

    Dispatcher.InvokeAsync(() => Main.Navigate(testPage), DispatcherPriority.Background);
    

I recommend handling the LoadCompleted event as the behavior is definitely more deterministic:

private void Butt1_Click(object sender, RoutedEventArgs e)
{
  combinedPage.TimerFrame.LoadCompleted  = SetMainFrame_OnLoadCompleted;
  combinedPage.TimerFrame.Navigate(null);
}

private void SetMainFrame_OnLoadCompleted(object sender, NavigationEventArgs e)
{
  combinedPage.TimerFrame.LoadCompleted -= SetMainFrame_OnLoadCompleted;
  Main.Navigate(testPage);
}

private void Butt2_Click(object sender, RoutedEventArgs e)
{
  Main.LoadCompleted  = SetFrameContents_OnLoadCompleted;
  Main.Navigate(null);
}

private void SetFrameContents_OnLoadCompleted(object sender, NavigationEventArgs e)
{
  Main.LoadCompleted -= SetFrameContents_OnLoadCompleted;
  combinedPage.Call();
  Main.Navigate(combinedPage);
}
  • Related