ItemsStackPanel 扩展到大型源的 Grid 容器之外

ItemsStackPanel extends beyond Grid container for a large source

更新信息 抱歉,我不得不查看我的问题和标题。但是,尽管我们进行了多次测试,但问题的缩小并不容易追踪。您会在下面找到导致无法解释的错误的新代码,以及由于遗留原因而留在此处的旧版本问题。这个问题很容易重现。

更新代码Listview 绑定到 ObservableCollection 并用大量项目填充它,在我们的例子中超过 30k 左右。最终结果是,随着项目数量的增加,ListView 中的 ItemsStackPanel 会扩展到包含的 Grid 之外。它发生在 ItemsStackPanel 的两个方向,但当方向设置为 Horizontal.

时更容易发生

我们的问题是如何解决这个问题,即在不失去虚拟化的情况下避免流出包含网格?

请注意,我们需要处理如此多的文件,因为我们正在构建一个照片应用程序,通常需要显示数万个 images/videos。 UWP 应用程序实际上在除此错误之外的所有问题上都对此做出了很好的响应。

谢谢

XAML

    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <Grid Grid.Row="1" Background="LightGreen">
        <ListView
          ItemsSource="{x:Bind Items}"
          ScrollViewer.HorizontalScrollMode="Enabled"
          ScrollViewer.HorizontalScrollBarVisibility="Visible" HorizontalAlignment="Left">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="Not working"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Grid>

C#

    public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        Items = new ObservableCollection<string>();
        for (int i = 0; i < 30000; i++)//Increase length here
        {
            Items.Add($"Item 1");
        }
    }
    public ObservableCollection<string> Items { get; }
}

旧代码(跳过)

以下代码显示 2 ListView,只有一处不同:

第一个 ListView 将 ItemsSource 绑定到 CollectionViewSource.View,其源设置为 FileInformationFactory。下面的 UI 提供了一个按钮,可以选择 100 多张图片来填充此 ListView(请参阅编辑部分)。

第二个 ListView 将 ItemsSource 绑定到另一个 CollectionViewSource.View,其源设置为普通的 ObservableCollection

结果

只要 ItemsStackPanel 的方向设置为 Horizontal,第一个 ListView 就会超出网格容器。切换 ItemsSource 无法解决此问题,因此表明此问题特定于 FileInformationFactory.GetVirtualizedFilesVector() 方法。

问题

如何在不失去虚拟化的情况下解决这个问题?

谢谢

XAML

    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.ColumnSpan="1" Orientation="Horizontal">
        <AppBarButton Icon="Folder"
                      LabelPosition="Collapsed"
                      Click="FolderPickerButton_Click"/>
    </StackPanel>
    <Grid Grid.Row="1" Background="LightGreen">
        <ListView
              ItemsSource="{x:Bind CollectionViewSource.View, Mode=OneWay}"
              ScrollViewer.HorizontalScrollMode="Enabled"
              ScrollViewer.HorizontalScrollBarVisibility="Visible" HorizontalAlignment="Left">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="Not working" TextWrapping="WrapWholeWords"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
    <Grid Grid.Row="2" Background="Yellow">
        <ListView
              ItemsSource="{x:Bind WorksCollectionViewSource.View, Mode=OneWay}"
              ScrollViewer.HorizontalScrollMode="Enabled"
              ScrollViewer.HorizontalScrollBarVisibility="Visible" HorizontalAlignment="Left">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="Working" TextWrapping="WrapWholeWords"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Grid>

C#

    public sealed partial class Scenario5 : Page
{
    public Scenario5()
    {
        this.InitializeComponent();
        Items = new ObservableCollection<string>();
        for (int i = 0; i < 30000; i++)
        {
            Items.Add($"Item 1");
        }
        CollectionViewSource = new CollectionViewSource();
        WorksCollectionViewSource = new CollectionViewSource();
    }

    private StorageFolder _folder;
    private QueryOptions _queryOptions;
    private StorageFileQueryResult _query;
    private FileInformationFactory _fileInformationFactory;

    public CollectionViewSource CollectionViewSource { get; set; }

    public CollectionViewSource WorksCollectionViewSource { get; set; }

    public ObservableCollection<string> Items { get; }

    private async void FolderPickerButton_Click(object sender, RoutedEventArgs e)
    {
        var _pickedFolder = await PickFolderAsync();
        if (_pickedFolder == null)
        {
            return;
        }

        _folder = _pickedFolder;
        _queryOptions = new QueryOptions
        {
            FolderDepth = FolderDepth.Deep,
            IndexerOption = IndexerOption.UseIndexerWhenAvailable,
        };

        _query = _folder.CreateFileQueryWithOptions(_queryOptions);

        _fileInformationFactory = new FileInformationFactory(_query, ThumbnailMode.SingleItem, 160,
            ThumbnailOptions.UseCurrentScale, delayLoad: false);

        var _vector = _fileInformationFactory.GetVirtualizedFilesVector();

        CollectionViewSource.Source = _vector;

        WorksCollectionViewSource.Source = Items;
    }

    private static async Task<StorageFolder> PickFolderAsync()
    {
        var folderPicker = new FolderPicker
        {
            SuggestedStartLocation = PickerLocationId.Desktop,
            ViewMode = PickerViewMode.Thumbnail
        };

        folderPicker.FileTypeFilter.Add("*");

        var _pickedFolder = await folderPicker.PickSingleFolderAsync();
        return _pickedFolder;
    }

}

编辑

问题已缩小。这不是 FileInformationFactory 的问题。 ObservableCollection 也会出现此问题 有大量物品,在我的情况下大约 30k,请参阅后面代码中的 ctor。在这个数字以下,所有项目都在网格内,在这个数字之上,他们离开 parent 容器。

要用 ListView 来规避这个问题,可以 Clip Visual 这样

 var visual = ElementCompositionPreview.GetElementVisual(listView);
            visual.Clip = Window.Current.Compositor.CreateInsetClip();

其中 listViewListView 的名称。

感谢 ranjeshj 在 Github https://github.com/microsoft/microsoft-ui-xaml/issues/1876