views:

72

answers:

1

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 http://stackoverflow.com/questions/716472/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.

A: 

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.

Sam
I think my main problem is the actual loading of the images from disk into memory. I'm not sure if there is anything i can really fix here, is there? I load all of the images into a list, then i generate all of the wrap items with references to the BitmapImages from the list. I can clear all of the wrap children and re create them pretty fast, but the loading itself takes multiple seconds. I tried a Parallel.For loop to load the images to the list, but it caused them to load a lot slower, probably due to async disk access.