NLuburić
NLuburić

Reputation: 922

WPF: ListView with Images from a folder

I'm sort of new to WPF, but I have to do this and it's taking a lot of my time. I've searched for a solution but there are many alternative solutions and I honestly don't understand most of this. I have this XAML code:

<ListView Name="Thumbnails">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}" Height="30" Width="30" Margin="5"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

As well as this codebehind:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    DirectoryInfo folder = new DirectoryInfo(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + @"\SlikeSportista\");
    FileInfo[] images = folder.GetFiles("*.jpg");
    foreach (FileInfo img in images)
    {
        Thumbnails.Items.Add(img);
    }
}

I've also tried this line of code in the foreach loop:

Thumbnails.Items.Add(System.Drawing.Image.FromFile(img.FullName));

In both cases the items are added, but the images are not displayed correctly, or rather, at all. You can select them, and there are the same amount of elements as there are in the folder, but there is no display.

Another question (less important one) would be how to display the images in squares instead of rows. Basically I want to have about 4 or so images per row, but now I have only 1 element per row, stretched all the way (although I can't see what is being displayed).

Upvotes: 2

Views: 14353

Answers (2)

Jonathan Perry
Jonathan Perry

Reputation: 3053

As a general rule, you should keep in mind that adding images in the code-behind file (.xaml.cs file) is bad practice. In WPF there is a very widely used and common design pattern called MVVM (Model-View-ViewModel) which you should familiarize with and use. In your case, you should have had a ViewModel class containing an IEnumerable<BitmapImage> property that contains the images you wish to display in your ListView.

For example, let's say your ViewModel class is called ImagesViewModel and your view is ImagesView:

ImagesViewModel will have a property called:

ObservableCollection<BitmapImage> Images

ImagesView will contain:

<ListView Name="Thumbnails" ItemsSource="{Binding Images}">
<ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <UniformGrid Columns="4"/>
    </ItemsPanelTemplate>
</ListView.ItemsPanel>
...

Now, if you add / remove images to Images, they will be automatically added / removed from your list view (you have to implement the INotifyPropertyChanged interface in your view model and you're done).

Upvotes: 0

Clemens
Clemens

Reputation: 128061

In your first attempt, you're adding FileInfo objects to the ListView's items collections. These aren't automatically converted to ImageSource items, as required by the binding in your DataTemplate. Add the FileInfo's FullName instead:

foreach (FileInfo img in images)
{
    Thumbnails.Items.Add(img.FullName);
}

In your second attempt the problem is that you add instances of System.Drawing.Image, which is not part of WPF, but WinForms, and will also not be converted automatically. You may use BitmapImage instead:

foreach (FileInfo img in images)
{
    Thumbnails.Items.Add(new BitmapImage(new Uri(img.FullName)));
}

The difference between both solutions is that in the second one you manually create image objects, whereas the first one relies on automatic conversion from string to ImageSource, which is built into WPF as a TypeConverter.


A solution for your second question would be to replace the ListView's ItemsPanel, perhaps by a UniformGrid:

<ListView Name="Thumbnails">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="4"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    ...
</ListView>

Upvotes: 4

Related Questions