Windows 未激活的页面仍会在UWP应用程序中发出事件

Windows 未激活的页面仍会在UWP应用程序中发出事件,windows,uwp,Windows,Uwp,我在UWP Windows 10应用程序中观察到一个奇怪的行为。即使我从一个页面返回并卸载了该页面,该页面仍然会继续发出事件。例如,我导航的旧页面仍然会发出“LayoutUpdated”事件,即使我转到完全不同的页面 我准备了一个简单的示例来演示这一点(代码如下)。这很简单: 共有2页:主页和示例页。您可以从主页转到ExamplePage,也可以从ExamplePage转到主页 每次导航到ExamplePage时,都会为新创建的页面提供一个新ID(页面不会被缓存) ExamplePage中

我在UWP Windows 10应用程序中观察到一个奇怪的行为。即使我从一个页面返回并卸载了该页面,该页面仍然会继续发出事件。例如,我导航的旧页面仍然会发出“LayoutUpdated”事件,即使我转到完全不同的页面

我准备了一个简单的示例来演示这一点(代码如下)。这很简单:

  • 共有2页:主页和示例页。您可以从主页转到ExamplePage,也可以从ExamplePage转到主页

  • 每次导航到ExamplePage时,都会为新创建的页面提供一个新ID(页面不会被缓存)

  • ExamplePage中的网格发出LayoutChanged事件。事件处理程序向调试控制台写入一个文本,如:“网格布局在第0页上更新”。0是我给该页面的页面ID

  • 如果来回几次,您将看到旧页面仍然向控制台写入布局更新的文本。例如,如果我转到ID为3的页面,它会写入控制台:

网格布局已在第0页上更新

网格布局在第3页更新

网格布局在第1页更新

网格布局在第2页更新

请注意,旧页面仍会更新其布局。旧页面不应再发出任何事件,但它们会继续发出事件,尽管无法再导航到它们并且它们已卸载

以下是代码,共有5个文件,只需在VS2015中创建一个新的UWP项目,然后:

MainPage.xaml


导航
MainPage.xaml.cs

使用Windows.UI.Xaml;
使用Windows.UI.Xaml.Controls;
名称空间App7
{
公共密封部分类主页面:第页
{
私人应用程序;
公共主页()
{
this.InitializeComponent();
app=(app)Application.Current;
}
私有无效导航按钮\单击(对象发送者,路由目标)
{
var viewModel=newexamplepageviewmodel(app.GetPageId());
Frame.Navigate(typeof(ExamplePage)、viewModel);
}
}
}
ExamplePage.xaml


回去
ExamplePage.xaml.cs

使用系统诊断;
使用Windows.UI.Xaml;
使用Windows.UI.Xaml.Controls;
使用Windows.UI.Xaml.Navigation;
名称空间App7
{
公共密封部分类示例第页:第页
{
私有示例页面视图模型视图模型;
公共示例页()
{
this.InitializeComponent();
}
受保护的覆盖无效OnNavigatedTo(NavigationEventArgs e)
{
如果(e.NavigationMode==NavigationMode.New||
e、 NavigationMode==NavigationMode.Back)
{
viewModel=(ExamplePageViewModel)e.参数;
DataContext=viewModel;
}
}
私有无效导航按钮\单击(对象发送者,路由目标)
{
Frame.GoBack();
}
私有void MainGrid\u布局已更新(对象发送方,对象e)
{
Debug.WriteLine(“网格布局在页面“+viewModel?.PageId.ToString()上更新”);
}
}
}
示例PageViewModel.cs

使用System.ComponentModel;
使用Windows.UI.Xaml;
名称空间App7
{
公共类示例PageViewModel:INotifyPropertyChanged
{
私人应用程序;
私有int-pageId;
公共事件属性更改事件处理程序属性更改;
公共整数页ID
{
得到
{
返回pageId;
}
}
公共示例PageViewModel(int pageId)
{
app=(app)Application.Current;
this.pageId=pageId;
}
}
}
注意:viewmodel只是为了清楚地看到哪个页面仍在发出事件。您可以删除viewmodel,但它不会改变问题。

如果垃圾收集器未收集该元素,则会为不在主可视树中的元素激发该事件。因为我们不知道Frame类的实现,所以我们不知道它是如何引用它实例化的页面的(可能它持有对卸载页面的引用的时间比需要的时间稍长?谁知道呢)

这与垃圾收集器的异步性质一起意味着旧页面仍然可以引发LayoutUpdated事件,直到事件处理程序被删除或对象被GC收集。在您的示例中,GC根本没有时间收集您的旧页面


如果内存中还有几个复杂的页面,这难道不会使应用程序性能下降吗?我可以在我的应用程序中看到,数十个复杂页面仍在触发LayoutUpdate事件,因此所有控件都在计算每个页面导航的大小,对吗

这些页面应该在下一个垃圾收集周期中由GC收集,这将在需要时自动发生。您可以使用
GC.Collect()
强制垃圾收集,但我不建议这样做。GC比您(通常)更擅长确定执行收集的时间

LayoutUpdated事件将在所有元素上触发(我认为),而不管该特定元素的布局是否已更改。如果您阅读了该事件的详细信息,它会说明,如果元素的布局受同级(例如)影响,则有必要执行此操作

布局系统相当优化。我不认为每次元素收到LayoutUpdated事件时都会对所有元素执行复杂的布局传递,所以我不担心这一点。但是,当元素不可见时,确保在这些事件处理程序中没有进行不必要的计算是很重要的

这很好地解释了LayoutUpdated事件。这是一个静态事件,