2016-10-18 2 views
1

私のUWP Windows 10アプリケーションで奇妙な動作が確認されました。ページから戻るとページがアンロードされても、ページは引き続きイベントを放出します。たとえば、私がナビゲートした古いページは、まったく別のページに行っても「LayoutUpdated」イベントを出します。UWPアプリケーションでまだページが表示されない

私はこれを実証するための最小限の例を用意しました(コードは以下です)。それはかなり簡単です:

  • メインページとサンプルページがあります。 MainPageからExamplePageに移動し、ExamplePageからMainPageに戻ることができます。

  • ExamplePageに移動するたびに、新しく作成されたページに新しいIDが与えられます(ページはキャッシュされません)。

  • ExamplePageのグリッドはLayoutChangedイベントを送出します。そして、イベントハンドラは、「ページ0で更新されたグリッドレイアウト」のように、デバッグコンソールにテキストを書き込みます。 0は私がそのページに与えたページIDです。

  • 数回前に戻ると、古いページでもレイアウトの更新テキストがコンソールに書き込まれます。私はID 3でページに移動した場合、それはコンソールに書き込みます:更新されたページ3

    グリッドレイアウトに更新されたページ0

    グリッドレイアウトに更新

グリッドレイアウトページ1つの

グリッドレイアウトは、2

ないページに更新します古いページはまだレイアウトを更新しています。古いページはもうイベントを発行してはいけませんが、もうイベントに出ることはなく、アンロードされます。ここで

は、ちょうどその新しいUWP VS2015でのプロジェクトと作成5つのファイルがあり、コードです:

MainPage.xamlをし

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 
    <Button x:Name="NavigationButton" 
      Click="NavigationButton_Click" 
      HorizontalAlignment="Center" 
      VerticalAlignment="Top" 
      Margin="0,20,0,0">Navigate</Button> 
</Grid> 

MainPage.xaml.cs

using Windows.UI.Xaml; 
using Windows.UI.Xaml.Controls; 

namespace App7 
{ 
    public sealed partial class MainPage : Page 
    { 
     private App app; 

     public MainPage() 
     { 
      this.InitializeComponent(); 

      app = (App)Application.Current; 
     } 

     private void NavigationButton_Click(object sender, RoutedEventArgs e) 
     { 
      var viewModel = new ExamplePageViewModel(app.GetPageId()); 
      Frame.Navigate(typeof(ExamplePage), viewModel); 
     } 
    } 
} 

ExamplePage.xaml

<Grid x:Name="MainGrid" 
     Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" 
     LayoutUpdated="MainGrid_LayoutUpdated"> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 
    <Button x:Name="NavigationButton" 
      Click="NavigationButton_Click" HorizontalAlignment="Center" 
      Margin="0,20,0,0">Go Back</Button> 
    <TextBlock Text="{Binding PageId}" 
       Grid.Row="1" 
       FontSize="30" 
       HorizontalAlignment="Center"></TextBlock> 
</Grid> 

ExamplePage.xaml.cs

using System.Diagnostics; 
using Windows.UI.Xaml; 
using Windows.UI.Xaml.Controls; 
using Windows.UI.Xaml.Navigation; 

namespace App7 
{ 
    public sealed partial class ExamplePage : Page 
    { 
     private ExamplePageViewModel viewModel; 

     public ExamplePage() 
     { 
      this.InitializeComponent(); 
     } 

     protected override void OnNavigatedTo(NavigationEventArgs e) 
     { 
      if (e.NavigationMode == NavigationMode.New || 
       e.NavigationMode == NavigationMode.Back) 
      { 
       viewModel = (ExamplePageViewModel)e.Parameter; 
       DataContext = viewModel; 
      } 
     } 

     private void NavigationButton_Click(object sender, RoutedEventArgs e) 
     { 
      Frame.GoBack(); 
     } 

     private void MainGrid_LayoutUpdated(object sender, object e) 
     { 
      Debug.WriteLine("grid layout updated on page " + viewModel?.PageId.ToString()); 
     } 
    } 
} 

ExamplePageViewModel.cs

using System.ComponentModel; 
using Windows.UI.Xaml; 

namespace App7 
{ 
    public class ExamplePageViewModel : INotifyPropertyChanged 
    { 
     private App app; 
     private int pageId; 

     public event PropertyChangedEventHandler PropertyChanged; 

     public int PageId 
     { 
      get 
      { 
       return pageId; 
      } 
     } 

     public ExamplePageViewModel(int pageId) 
     { 
      app = (App)Application.Current; 
      this.pageId = pageId; 
     } 
    } 
} 

注:のviewmodelはちょうどまだイベントをemitingされたページはっきりと確認することです。ビューモデルを削除することはできますが、問題は変わりません。

答えて

1

メインビジュアルツリーにない要素に対しては、ガベージコレクタによって要素が収集されていない場合、イベントが発生します。Frameクラスの実装についてはわからないので、インスタンス化するページをどのように参照するのかはわかりません(必要以上に長い間、アンロードされたページへの参照を保持しているかもしれません)。

ガベージコレクタの非同期性と合わせて、イベントハンドラが削除されるか、GCによってオブジェクトが収集されるまで、古いページでもLayoutUpdatedイベントが発生する可能性があります。あなたの例では、GCは単にあなたの古いページを収集することに慣れていません。


あなたはまだメモリ上のいくつかの複雑なページを持っている場合は、このアプリのパフォーマンスの低下がありませんか?私のアプリでは、何十もの複雑なページがLayoutUpdatedイベントを発生させていることが分かるので、すべてのコントロールはすべてのページナビゲーションでサイズを計算していますよね?

これらのページは、次のガベージコレクションサイクル中にGCによって収集される必要があります。これは、必要なときに自動的に発生します。 GC.Collect()でガベージコレクションを強制できますが、これはお勧めしません。 GCは、あなたが(一般的に)よりも収集を実行する時間を判断するのに優れています。

LayoutUpdatedイベントは、特定の要素のレイアウトが変更されているかどうかにかかわらず、すべての要素で発生します(私は思う)。そのイベントのdocsを読むと、要素のレイアウトが兄弟(例えば)の影響を受ける場合にこれを行う必要があることが説明されています。

レイアウトシステムは非常に最適化されています。 LayoutUpdatedイベントを受け取るたびに、複雑なレイアウトパスがすべての要素に対して実行されるとは思わないので、私はそれについて心配しません。ただし、要素が表示されていないときにこれらのイベントハンドラで不要な計算を実行していないことを確認することが重要です。

Here is another pageこれは、LayoutUpdatedイベントを非常にうまく説明しています。 静的イベントです。レイアウトが更新された任意の要素がある場合に発生します。

LayoutUpdatedイベントに不要なコードを挿入しないでください。または、逆方向にナビゲートする際にアンバインドしますが、それでもすべてのコントロールのサイズを独自に再計算しますか?

LayoutUpdatedイベントに対するあなたの応答は、「私に影響を与えるかもしれない、または影響を与えないかもしれないレイアウトのどこかの要素が更新されたもの」でなければなりません。あなたはイベントをバインド解除することができます。または、this.Parentまたはthis.Frameがヌルであり、その場合はページがフレームにない場合には、それを確認することができます。

コントロールがビジュアルツリーに存在しない場合でも、他のイベントが発生しますか?どのようにそのようなイベントのリストを見つけることができますか?

わかりません。あなたは、これらの状況のた​​めにあなたのアプリをテストする必要があります。各イベントハンドラにブレークポイントを設定して、起動するかどうかを知る必要があります。

+0

よろしくお願いいたします。複雑なページがまだメモリに残っている場合、これはアプリのパフォーマンスを低下させませんか?私のアプリでは、何十もの複雑なページがLayoutUpdatedイベントを発生させていることが分かるので、すべてのコントロールはすべてのページナビゲーションでサイズを計算していますよね?私はLayoutUpdatedイベントに不要なコードを入れないでしょう、または私は戻ってナビゲートする際にそれらをアンバインドしますが、それでも、すべてのコントロールのサイズを独自に再計算しますか? もう1つ質問:コントロールがビジュアルツリーにない場合でも、他のイベントが発生しますか?どのようにそのようなイベントのリストを見つけることができますか? –

関連する問題