Search code examples
c#wpfxamllistview

How to make WPF listview display Images and Labels dynamically


I'm having quite a difficult time trying to create the UI for a WPF Window. I'm trying to display (dynamically) a bunch of Movie Posters with the name of the movie directly under the image. ItemsSource is assigned to a list of Images via foreach iteration. The Image files themselves may be different sizes, but as shown below I will be setting a uniform size.

Basically, my goal is for it to look something like this:

enter image description here

So far, My code only displays a window with one large horizontal row(?) with the image in the center and no label. Here's my XAML code:

<Window x:Name="TVWindow" x:Class="PACS_Pre_Alpha.TV"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="TV" Height="746" Width="1000" ResizeMode="NoResize">
<Grid x:Name="TVGrid">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
    </Grid.RowDefinitions>
    <ListView x:Name="TvBox" HorizontalAlignment="Left" Height="648" VerticalAlignment="Top" Width="994" Grid.Row="5" Grid.Column="5">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="5" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Vertical" VerticalAlignment="Stretch">
                    <Image Source="{Binding ImageData}" HorizontalAlignment="Center" VerticalAlignment="Top" />
                    <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

My movies are added with this C# code:

foreach (string tvf in ContentFiles)
{
            string ContentTitle = System.IO.Path.GetFileNameWithoutExtension(tvf);
            MovieData cnt = new MovieData();
            cnt.ImageData = LoadImage(ActualImage);
            cnt.Title = ContentTitle;
            ContentDataList.Add(cnt);

}
        TvBox.ItemsSource = ContentDataList;

Edit: I have changed my XAML Markup as @MarkFeldman suggested, but now nothing appears. Edit: It currently looks like this: enter image description here


Solution

  • You're going to provide more info about the data itself i.e. what's it's format, how are you assigning it to the ItemsSource etc. For one thing you're not setting the ItemTemplate, so you might want to look at that first. For example if you have a class containing your movie data that looks like this:

    public class MovieData
    {
        private string _Title;
        public string Title
        {
            get { return this._Title; }
            set { this._Title = value; }
        }
    
        private BitmapImage _ImageData;
        public BitmapImage ImageData
        {
            get { return this._ImageData; }
            set { this._ImageData = value; }
        }
    
    }
    

    Then you would display it with something like this:

    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Vertical" VerticalAlignment="Stretch">
                <Image Source="{Binding ImageData}" HorizontalAlignment="Center" VerticalAlignment="Top"/>
                <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
    

    UPDATE:

    Sorry, I thought it was obvious that you still needed to use a UniformGrid. Here is what your full XAML should look like:

    <ListView x:Name="TvBox" HorizontalAlignment="Stretch" VerticalAlignment="Top">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="5" HorizontalAlignment="Stretch"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
                    <Image Source="{Binding ImageData}" HorizontalAlignment="Stretch" VerticalAlignment="Top" Stretch="UniformToFill" />
                    <TextBlock Text="{Binding Title}" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    I've already provided you with the MovieData class, so here's what your Window code should look like:

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
    
            this.TvBox.ItemsSource = new MovieData[]
            {
                new MovieData{Title="Movie 1", ImageData=LoadImage("image.jpg")},
                new MovieData{Title="Movie 2", ImageData=LoadImage("image.jpg")},
                new MovieData{Title="Movie 3", ImageData=LoadImage("image.jpg")},
                new MovieData{Title="Movie 4", ImageData=LoadImage("image.jpg")},
                new MovieData{Title="Movie 5", ImageData=LoadImage("image.jpg")},
                new MovieData{Title="Movie 6", ImageData=LoadImage("image.jpg")}
            };
        }
    
        // for this code image needs to be a project resource
        private BitmapImage LoadImage(string filename)
        {
            return new BitmapImage(new Uri("pack://application:,,,/" + filename));
        }
    }
    

    In this example I'm assuming there is an image in your project called "image.jpg" which has been set to build action "Resource", if your images come from elsewhere then you'll need to modify the LoadImage code accordingly.