سؤال

I am experiencing a very strange bug in my c++/cx XAML app: I have a back-button that checks if the progress you made is saved and (in case it isn't) pops up a flyout that lets you save or leave without saving. This is done with this->frame->goBack() in both cases, however:

When the progress was saved, the app halts at a __debugbreak() however, when goBack() is called by the button on the flyout, everything works out fine. Why could that possibly be the case?

Things that might help you:

The app is based on the "Blank App" template, the pages itself are based on the "Basic Page" template provided by Visual Studio 2013

The Controls in BoardPage.xaml are defined as follows:

<AppBarButton x:Name="backButton" Icon="Back" Height="95"
    Click="backButton_Clicked">
    <AppBarButton.Resources>
        <Flyout x:Key="WarningFlyoutBase">
            <Grid Height="150" Width="200">
                <Grid.RowDefinitions>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                </Grid.RowDefinitions>
                <TextBlock Text="Unsaved progress, what do you want to do?"
                    HorizontalAlignment="Center" TextWrapping="Wrap" 
                    FontSize="14" Margin="4,10" TextAlignment="Center"/>
                <Button x:Name="WarningSaveButton"
                    Content="Save now." 
                    Grid.Row="1" HorizontalAlignment="Center"
                    Click="WarningSaveButton_Clicked"/>
                <Button x:Name="WarningLeaveButton"
                    Content="Leave without saving." 
                    Grid.Row="2" HorizontalAlignment="Center" 
                    Click="WarningLeaveButton_Clicked"/>
            </Grid>
        </Flyout>
    </AppBarButton.Resources>
    <AppBarButton.Flyout>
        <StaticResource ResourceKey="WarningFlyoutBase"/>
    </AppBarButton.Flyout>
</AppBarButton>

so these three controls (backButton, WarningSaveButton and WarningLeaveButton) all have their respective Clicked event handlers, though only two of them are relevant right now:

backButton:

void Tackle::BoardPage::backButton_Clicked(Platform::Object^ sender,
    Windows::UI::Xaml::RoutedEventArgs^ e)
{
    if (saved && Frame->CanGoBack) /* saved is a bool */
        this->Frame->GoBack();
    else
        backButton->Flyout->ShowAt((FrameworkElement^)sender);
}

Note: I also tried replacing GoBack() with Navigate(TypeName(CreateGamePage::typeid)), but that didn't help.

WarningLeaveButton:

void Tackle::BoardPage::WarningLeaveButton_Clicked(Platform::Object^ sender, 
    Windows::UI::Xaml::RoutedEventArgs^ e)
{
    if (Frame->CanGoBack)
        this->Frame->GoBack();
}

other strange stuff:

I tried to examine the reasons for this crash/debugbreak for quite some time, and found the following:

  • the page in question is navigated to with this->Frame->Navigate(TypeName(BoardPage::typeid), ref new CGameSession(job));. leaving out the second argument fixes the crash magically.

  • When the breakpoint is triggered in App.g.hpp, opening a watch on the value errorMessage reveales:

    "Placement target needs to be in visual tree."

    How come the Flyout is in the visual tree, but the Button it's been attached to isn't?

  • The target page is in fact constructed, but the NavigationHelper->OnNavigatedTo(e) method fails in the last line LoadState(this, ref new LoadStateEventArgs(e->Parameter, safe_cast<IMap<String^, Object^>^>(frameState->Lookup(_pageKey))));, wich seems paritularly odd, because LoadState() gets called easily and only contains two (void) typecasts. (I have not modified a single one of these methods.)
هل كانت مفيدة؟

المحلول

The Problem here lies within the way flyouts and event handlers work with XAML-Controls, there are 2 mayor points that cause my code to crash:

  1. Flyouts are always shown when the button they're attached to is clicked or tapped.
  2. Event Handlers seem to be executed before the flyout gets shown.

What follows from this?

Well, when the backButton is pressed, backButton_Clicked gets triggered first. When the game is not saved, the flyout opens and you can go back without a Problem. But if the game was saved previously, the event handler immediately calls this->Frame->GoBack(), wich does exactly what it should, constructs the previous page, loads its state and draws it.
BUT after that, it tries to open the flyout, and this is the problem here: The flyout doesn't exist anymore, hence the strange error Message.

I am kind of embarassed it took me this long to figure it out, but maybe someone will find this useful in the future, so I'm going to leave this question here.

Workaround:

The workaround I'm using for this now is to move the FlyoutBase to the <Page.Resources> and attach it to a button with a size of 0x0 px. Triggering the flyout then done like so: Flyout::ShowAttachedFlyout(InvisibleButton);

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top