Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF: How to load lots of large images fast into wrappanel?

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.

like image 814
user380527 Avatar asked Jun 30 '10 21:06

user380527


1 Answers

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.

like image 123
Samuel Avatar answered Oct 01 '22 01:10

Samuel