为什么程序崩溃通过向listview添加大量图像?

时间:2017-12-02 19:05:00

标签: c# wpf listview

这是我的XAML:

NaN

这样我就可以将项目添加到ListView:

<ListView MouseDoubleClick="ImageList_MouseDoubleClick" Name="ImageList" Background="#353535" Grid.Row="2" Margin="0 5 0 0">
<ListView.ItemTemplate>
    <DataTemplate>
        <Grid Cursor="Hand" Width="200" Height="130" VerticalAlignment="Center" HorizontalAlignment="Left">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <Image Source="{Binding ImgPath}" Grid.Row="0"/>
            <Label FontSize="14" Foreground="White" Grid.Row="1" HorizontalAlignment="Center" Content="{Binding Name}"/>
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <WrapPanel Orientation="Horizontal" Width="{Binding Path=ActualWidth, ElementName=ImageList}"/>
    </ItemsPanelTemplate>
</ListView.ItemsPanel>

这段代码可以很好地处理一些图像,但是当我尝试打开一个包含100多张图片的文件夹时,我的程序崩溃了。我做错了什么或我能做些什么来优化我的程序?

2 个答案:

答案 0 :(得分:1)

您可以使用视图模型异步加载缩略图图像文件,并通过设置npm ejectDecodePixelWidth属性来限制其大小。

DecodePixelHeight

你会绑定到这样的视图模型:

public class ImageData
{
    public string Name { get; set; }
    public ImageSource ImageSource { get; set; }
}

public class ViewModel
{
    public ObservableCollection<ImageData> Images { get; }
        = new ObservableCollection<ImageData>();

    public async Task LoadFolder(string folderName, string extension = "*.jpg")
    {
        Images.Clear();

        foreach (var path in Directory.EnumerateFiles(folderName, extension))
        {
            Images.Add(new ImageData
            {
                Name = Path.GetFileName(path),
                ImageSource = await LoadImage(path)
            });
        }
    }

    public Task<BitmapImage> LoadImage(string path)
    {
        return Task.Run(() =>
        {
            var bitmap = new BitmapImage();

            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                bitmap.BeginInit();
                bitmap.DecodePixelHeight = 100;
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.StreamSource = stream;
                bitmap.EndInit();
                bitmap.Freeze();
            }

            return bitmap;
        });
    }
}

并填充它,例如在某些异步输入事件处理程序中,如:

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>
...
<ListBox ItemsSource="{Binding Images}"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Width="200" Height="130">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition Height="30"/>
                </Grid.RowDefinitions>
                <Image Source="{Binding ImageSource}"/>
                <TextBlock Grid.Row="1" Text="{Binding Name}"/>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

答案 1 :(得分:0)

经过一段时间,我发现了这个问题的几个解决方案。对我来说,有一些简单的方法,以及一些更难的方法。 我认为最简单的方法是: 在我的问题代码中,我们需要更改以下代码:

<ItemsPanelTemplate>
    <WrapPanel Orientation="Horizontal" Width="{Binding Path=ActualWidth, ElementName=ImageList}"/>
</ItemsPanelTemplate>

“WrapPanel”替换“VirtualizingStackPanel”。通过这种方式,程序将以用户@mkArtak建议的方式工作:

  

此外,在底部区域显示图像缩略图时,您应该使用某种虚拟化,因为您不想一次加载所有图像。感觉像很常见的场景,可以控制某个地方可用的控件,您可以重复使用。我们的想法是只加载每边可见+2的图像。然后,在用户滚动时加载其他任何内容。

据我所知,这是virtualization。当然,您可以调整虚拟化的方式。您可以在互联网上找到相关信息。

还有一个更难的方法:使用async \等待建议的用户@Clemens

  

您可以拥有一个以异步方式加载缩略图图像文件的视图模型,并通过设置DecodePixelWidth或DecodePixelHeight属性来限制其大小。

此外,我们可以使用这两种方式,我认为这将是最佳方式。

非常感谢大家帮忙找到解决这个问题的方法。