Связанные с UWP анимационные сбои после второго использования - PullRequest
0 голосов
/ 29 мая 2018

Я связал две формы с событием щелчка, которое запускает связанную анимацию в обоих направлениях.Первый раз иду вперёд и назад всё работает отлично.Во второй раз он работает, но при попытке вернуться во второй раз происходит сбой приложения со следующим исключением:

System.ArgumentException: параметр неверен.Не удается запустить анимацию - элемент источника не находится в дереве элементов.

Это происходит в первой строке SecondPage_BackRequested, но только во втором выполнении.Первое исполнение работает и отлично анимирует.

Любая помощь будет принята с благодарностью.Я пролистал документацию по подключенной анимации, и, насколько я могу судить, это то, как она должна использоваться, но я не могу найти ссылку на эту ошибку, возникающую где-либо.

Мой код (MainPageViewModel опущено какэто не актуально, но может быть добавлено по запросу):

MainPage.xaml

<Page
    x:Class="AnimTest.Views.Main.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:models="using:AnimTest.Models"
    xmlns:main="using:AnimTest.Views.Main"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          Padding="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Text="AnimTest"/>
        <GridView x:Name="TileGrid"
                  Grid.Row="1"
                  IsItemClickEnabled="True"
                  ItemsSource="{x:Bind ViewModel.Tiles, Mode=OneWay}"
                  ItemClick="GridView_ItemClick"
                  Loaded="TileGrid_Loaded">
            <GridView.ItemTemplate>
                <DataTemplate x:DataType="models:Tile">
                    <Border x:Name="TileBorder"
                            Background="Red"
                            MinHeight="150"
                            MinWidth="200">
                        <StackPanel Orientation="Vertical"
                                    VerticalAlignment="Center">
                            <SymbolIcon Symbol="World"/>
                            <TextBlock Text="{x:Bind Name}"
                                       HorizontalTextAlignment="Center"/>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>
</Page>

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPageViewModel ViewModel => (MainPageViewModel)DataContext;

    public MainPage()
    {
        InitializeComponent();

        DataContext = new MainPageViewModel();
    }

    private void GridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        TileGrid.PrepareConnectedAnimation("borderIn", e.ClickedItem, "TileBorder");
        Frame.Navigate(typeof(SecondPage));
    }

    private async void TileGrid_Loaded(object sender, RoutedEventArgs e)
    {
        var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("borderOut");
        if (animation != null)
        {
            var success = await TileGrid.TryStartConnectedAnimationAsync(animation, ViewModel.Tiles[0], "TileBorder");
        }
    }
}

SecondPage.xaml

<Page
    x:Class="HomeTiles.Views.Thermostat.ThermostatPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HomeTiles.Views.Thermostat"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Border x:Name="MainBorder"
            Background="Red">
    </Border>
</Page>

SecondPage.xaml.cs

public sealed partial class SecondPage : Page
{
    public SecondPage()
    {
        this.InitializeComponent();
        SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
        SystemNavigationManager.GetForCurrentView().BackRequested += SecondPage_BackRequested;
    }

    private void SecondPage_BackRequested(object sender, BackRequestedEventArgs e)
    {
        ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("borderOut", MainBorder);
        Frame?.GoBack();
        e.Handled = true;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("borderIn");
        animation?.TryStart(MainBorder);
    }
}

1 Ответ

0 голосов
/ 30 мая 2018

Проблема на самом деле не в подключенной анимации, а в событиях навигации.

При первом достижении SecondPage вы подключаете событие BackRequested, а когда возвращаетесь, все в порядке.Однако обработчик события остается прикрепленным к событию даже после перехода из SecondPage.Это проблема, потому что как только вы снова перейдете к SecondPage, теперь чет будет зарегистрирован дважды.А также при первом запуске обработчика происходит сбой, так как первый обработчик подключен к предыдущему экземпляру страницы, и подключенная анимация уже прошла для этого.Наконец, из-за этого события страница останется в памяти навсегда, что может привести к серьезным утечкам памяти.

Решение довольно простое - вы должны убедиться, что не забыли отписаться от чётного обработчика, когда выпокидаете страницу, например, в методе OnNavigatedFrom и подписываетесь в методе OnNavigatedTo для большей ясности:

public sealed partial class SecondPage : Page
{
    public SecondPage()
    {
        this.InitializeComponent();

    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        base.OnNavigatedFrom(e);
        SystemNavigationManager.GetForCurrentView().BackRequested -= SecondPage_BackRequested;
    }

    private void SecondPage_BackRequested(object sender, BackRequestedEventArgs e)
    {
        ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("borderOut", MainBorder);
        Frame?.GoBack();
        e.Handled = true;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
        SystemNavigationManager.GetForCurrentView().BackRequested += SecondPage_BackRequested;

        var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("borderIn");
        animation?.TryStart(MainBorder);
    }
}

Чтобы избежать подобных проблем, я обычно настраиваю событие BackRequested дляцелое приложение в App и подписывайте его только один раз при запуске.Затем вы можете поместить подключенный код анимации в метод OnNavigatedFrom вместо того, чтобы подписываться на BackRequested:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);       
    ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("borderOut", MainBorder);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...