WPF:如何将大量大图像快速加载到wrappanel中?

发布于 2024-09-07 13:21:09 字数 2215 浏览 0 评论 0 原文

我有大约 45 个相当大的图像(大约 680x1000),需要加载到一个简单的用户控件(带有填充、图像、文本块和 2 个侧面矩形的圆形背景边框)中,然后显示在包装面板中。虚拟化在这里并没有真正的帮助,因为图像在程序加载时都是可见的。

我知道在 BitmapImage init 内部我可以设置解码像素宽度,这确实有一点帮助,但是我喜欢将它们全部加载为全尺寸,因为我希望能够使用滑块调整图像大小而不损失质量(这部分工作速度很快大多数情况下)。我知道一种可能性是将解码宽度设置为某个数字,我将其设置为最大可视大小可能会有所帮助。

我尝试了 ​​如何在后台加载图像? (第一个答案),但是它导致程序需要更长的时间来加载!

有什么想法吗?

当前加载代码:

BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
//bmp.DecodePixelWidth = 400;
bmp.UriSource = new Uri(file.FullName);
bmp.EndInit();
bmp.Freeze();
images.Add(bmp);

示例 XAML 代码:

        <Border x:Name="backBorder" Background="Black" Padding="2" Margin="3" CornerRadius="3,3,4,4" 
            BorderBrush="Black" BorderThickness="1"
            MouseEnter="backBorder_MouseEnter" MouseLeave="backBorder_MouseLeave" MouseLeftButtonUp="backBorder_MouseLeftButtonUp" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="16" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="15" />
        </Grid.ColumnDefinitions>
        <Image x:Name="imageBox" Stretch="Fill" Width="{Binding Path=ImageWidth, ElementName=me}" Height="{Binding Path=ImageHeight, ElementName=me}" />
        <Border x:Name="backRatingBorder" Grid.Column="1" Margin="3,0,0,0" BorderBrush="Blue" Background="White" BorderThickness="1"/>
        <Border x:Name="frontRatingBorder" Grid.Column="1" Margin="3,0,0,0" BorderBrush="Blue" Background="LightBlue" BorderThickness="1" VerticalAlignment="Bottom" Height="50"/>
        <TextBlock x:Name="textBlock" Grid.Row="1" Grid.ColumnSpan="2" TextAlignment="Center" Background="Transparent" Foreground="White" FontFamily="Segoe UI" FontWeight="SemiBold" FontSize="12" />
   </Grid>
</Border>

.

更新:

好吧,我最终通过在单个后台工作人员中运行加载图像循环来使其响应更快。加载每个图像后,调用 Dispacher.Invoke 来创建包装项。使用了一段时间后,我让它显示每个项目,因为它是在之前创建的相同时间内创建的。

I have about 45 decently large images (about 680x1000) that need to be loaded into a simple user control (rounded backborder with fill, image, textblock, and 2 side rectangles) and then displayed in a wrappanel. Virtualizing won't really help here since the images are to be all visible at program load.

I know inside of the BitmapImage init i can set the decodepixel width, which does help a little, however id like to load them all as full size since i want to be able resize the images with a slider without losing quality (this part works fast for the most part). I know one possibility would be to set the decodewidth to be some number which i set as the max viewable size could help.

I tried the multithreaded approach found in How do I load images in the background? (first answer), however it caused the program to take a LOT longer to load!

Any ideas?

Current load code:

BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
//bmp.DecodePixelWidth = 400;
bmp.UriSource = new Uri(file.FullName);
bmp.EndInit();
bmp.Freeze();
images.Add(bmp);

Sample XAML code:

        <Border x:Name="backBorder" Background="Black" Padding="2" Margin="3" CornerRadius="3,3,4,4" 
            BorderBrush="Black" BorderThickness="1"
            MouseEnter="backBorder_MouseEnter" MouseLeave="backBorder_MouseLeave" MouseLeftButtonUp="backBorder_MouseLeftButtonUp" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="16" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="15" />
        </Grid.ColumnDefinitions>
        <Image x:Name="imageBox" Stretch="Fill" Width="{Binding Path=ImageWidth, ElementName=me}" Height="{Binding Path=ImageHeight, ElementName=me}" />
        <Border x:Name="backRatingBorder" Grid.Column="1" Margin="3,0,0,0" BorderBrush="Blue" Background="White" BorderThickness="1"/>
        <Border x:Name="frontRatingBorder" Grid.Column="1" Margin="3,0,0,0" BorderBrush="Blue" Background="LightBlue" BorderThickness="1" VerticalAlignment="Bottom" Height="50"/>
        <TextBlock x:Name="textBlock" Grid.Row="1" Grid.ColumnSpan="2" TextAlignment="Center" Background="Transparent" Foreground="White" FontFamily="Segoe UI" FontWeight="SemiBold" FontSize="12" />
   </Grid>
</Border>

.

UPDATE:

Well i ended up making it more responsive by running the load image loop in a single background worker. After each image is loaded, Dispacher.Invoke is called to create the wrap item. After playing with it for a while i got it to show each item as it is created in the same time it took before.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

白衬杉格子梦 2024-09-14 13:21:09

如果您对整体性能(仅对图像的加载)感到满意,您可以尝试 这个多线程 UI 教程。 我设法让它很容易地工作,但如果你在循环中加载所有图像,它不会更新视觉效果,直到你完成加载所有图像。然而,UI 在此期间是响应式的,因为所有加载都在单独的线程上进行。

或者,如果您要循环加载所有图像,那么您可以尝试 Windows 窗体 DoEvents 方法的改进版本(向下滚动到示例)。您可以在加载每个图像后调用它,它会给 UI 有机会更新自身(处理用户交互等)。这是我在为我的项目加载地图图块时使用的方法,并且比第一种方法更容易。

If you're happy with the overall performance, just the loading of the images, you could try this Multithreaded UI tutorial. I managed to get it to work quite easily, but if you're loading all the images in a loop it won't update the visual until you've finished loading all of the images. The UI is responsive during this time, however, as all the loading is on a separate thread.

Alternativly, if you're loading all your images in a loop then you could try an improved version of Windows Forms DoEvents method (scroll down to the example). You'd call this after loading each image and it will give the UI a chance to update itself (process user interaction etc). This is the approach I used when loading map tiles for my project and is easier than the first.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文