GridView.ItemsSource 创建新文件时中断 Window

GridView.ItemsSource breaks when Creating a New Window

当我在导航到新页面时 运行 代码:

this.Frame.Navigate(typeof(BlankPage1), pages);

代码 运行 很好,并且在连接到 BlankPage1.xaml.cs

中的以下代码时会产生正确的输出
protected override void OnNavigatedTo(NavigationEventArgs e)
{
        _thumbnails = e.Parameter as ObservableCollection<ThumbnailImage>;
        FileView.ItemsSource = _thumbnails;
}

但是,当我 运行

CoreApplicationView newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
                Frame frame = new Frame();
                frame.MinHeight = 800;
                frame.MaxWidth = 400;
                frame.MinWidth = 200;
                frame.Navigate(typeof(BlankPage1), pages);
                Window.Current.Content = frame;
                Window.Current.Activate();
                newViewId = ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);

要创建一个新视图并导航到同一个 xaml 文件,它会抛出异常:"Exception thrown: 'System.Exception' in Viewer.exe"

但是,当 OnNavigatedTo 更改为

protected override void OnNavigatedTo(NavigationEventArgs e)
{
        _thumbnails = e.Parameter as ObservableCollection<ThumbnailImage>;
        //FileView.ItemsSource = _thumbnails;
}

没有错误,但没有填充 GridView。

class 是

public class ThumbnailImage
{
    public BitmapImage Source { get; set; }
    public StorageFile File { get; set; }

    public ThumbnailImage(BitmapImage source, StorageFile file)
    {
        Source = source;
        File = file;
    }
}

而 xaml 是

<Page Width="300" Height="850"
x:Class="Viewer.BlankPage1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Viewer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
    <GridView x:Name="FileView" Width="256" Height="850" Margin="0,20,0,0">
        <GridView.ItemTemplate>
            <DataTemplate x:DataType="local:ThumbnailImage">
                <Grid>
                    <Button FontFamily="Segoe MDL2 Assets" Content="&#xE894;" HorizontalAlignment="Left" VerticalAlignment="Top"/>
                    <Image CanDrag="True" Stretch="Uniform" Source="{x:Bind Source}" Height="100" Margin="10,40"/>
                    <Border Opacity=".8" Background="Black" VerticalAlignment="Bottom"/>
                </Grid>
            </DataTemplate>
        </GridView.ItemTemplate>
        <GridView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsWrapGrid Orientation="Horizontal" ScrollViewer.VerticalScrollBarVisibility="Visible"/>
            </ItemsPanelTemplate>
        </GridView.ItemsPanel>
    </GridView>
</ScrollViewer>
</Page>

有两个潜在问题:

  1. 您可能需要延迟对 frame.Navigate 的调用,直到应用程序加载完毕。在您的代码示例中,您尝试在将框架分配给 Window.Current.Content 之前进行导航。您可以尝试将其移动到 lambda 块中的最后一行。您甚至可能需要 运行 同步导航或至少在 UI 线程上导航。

  2. RunAsync 可能会导致问题。您可以使用 Dispatcher.Invoke 进行测试,看看是否仍然存在同样的问题。

在 UWP 中的两个不同视图之间传递 ObservableCollection(或任何其他基于 INotifyPropertyChangedINotifyCollectionChanged 的模型)不推荐.

原因是 UWP 中的每个视图都有自己的 UI 线程,这也是您需要使用新创建的视图的 Dispatcher 来导航到您的页面的原因。但是,当您使用 data-binding 时,对 collection 的任何更改都将执行 CollectionChanged,这将 运行 用于两个应用视图 。这将不可避免地使应用程序崩溃,因为 CollectionChangedPropertyChanged 会影响 UI,因此必须 运行 在特定视图的 UI 线程上 - 在这种情况下,每个视图有自己的 UI 线程。

因此,为确保避免这些问题,请创建一个新的 collection,以确保每个视图都有自己的 ObservableCollection 实例:

var secondaryViewPages = new ObservableCollection<ThumbnailImage>(pages);
frame.Navigate(typeof(BlankPage1), secondaryViewPages);

注意:如果 ThumbnailImage 实施 INotifyPropertyChanged,您还必须为collection。然而,在这种情况下,没有必要,因为 ThumbnailImage 是一个 POCO,它不会通知它对 UI.

的更改

另请注意: 不建议使用 collection 作为 Navigate 的参数,因为它会阻止您在应用程序期间序列化应用程序状态暂停,参见 docs:

Apps typically use GetNavigationState to serialize the frame’s state when the app suspends. You can do this directly in your app code or indirectly by using the SuspensionManager class generated by the Visual Studio templates. To enable frame state serialization using GetNavigationState, you must use only basic types for the navigation parameter, such as string, char, numeric, and GUID types. Otherwise GetNavigationState will throw an exception when the app suspends. The parameter can have other types if you do not use GetNavigationState.

相反,您可以以不同的方式在视图之间共享数据,例如拥有两个视图都可以访问的单例服务。