12

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

Luke Dinkler
  • 731
  • 5
  • 16
  • 36
  • 1
    can you post your full source code for this? I am wanting to do something similar and this would be a great building block for me! – StealthRT Oct 26 '16 at 18:10

2 Answers2

20

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.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • This looks good, but how do I set the individual movies from c# code via the `MovieData` class? – Luke Dinkler Mar 04 '16 at 13:36
  • I created a `MovieData` object for each movie I add (assigning the class variables) using a `foreach` iteration but nothing happens. – Luke Dinkler Mar 05 '16 at 16:10
  • I haven't seen your code, so no. It could be anything. If you add your code to the question I'll be happy to have a look at it again. – Mark Feldman Mar 07 '16 at 21:31
  • What code would you like to see? The assignment of the MovieData class objects? – Luke Dinkler Mar 07 '16 at 21:45
  • I get the same problem w/ the stuff packed into the middle but the images don't show up. Added my C# code. BTW I changed `ImageData` from `ImageSource` to `Image` – Luke Dinkler Mar 07 '16 at 22:27
  • That would be why your images aren't showing. `Image` is a windows UI control, it doesn't belong in a source data class like `MovieData`. `ImageSource` classes like `BitmapImage` contain the image data itself that the `Image` control displays on-screen. – Mark Feldman Mar 07 '16 at 22:33
  • Can't use Image source because I have to specify `Image` properties and events such as `MouseDown` – Luke Dinkler Mar 07 '16 at 22:34
  • 1
    You can declare those in XAML, you don't need to specifically set everything up in code-behind. In fact with WPF you should be relying on data-binding as much as possible, even if you're not doing full-blown MVVM. Just add `MouseDown="Image_MouseDown"` to your Image in the XAML and add the `Image_MouseDown` handler in your main window. The handler's `sender` parameter will point to the Image control and its DataContext will be set to the associated MovieData object. – Mark Feldman Mar 07 '16 at 23:41
  • Sorry to bother you but..... This sorta works. The Images now display. My concern would be that the Two Movies I have loaded show in the middle of the screen and when clicked show a blue selection (thing) that takes up an entire row. Can you help or would you like to see a screenshot? (this was actually my inital problem so it's not off topic) – Luke Dinkler Mar 08 '16 at 02:45
  • 1
    No problem....it's because you've used a `ListView`, which ultimately inherits `ItemsControl` but adds additional functionality like scrolling and item selection etc. If you don't want those features then change `ListView` to an `ItemsControl`. If you still want scrolling then you'll also have to wrap it in a ``. – Mark Feldman Mar 08 '16 at 04:06
1

I have done something very similar with UniformGrid I see you did not set the Rows of your UniformGrid. I did this In my Game App. Good approach but difficult to get right. Set an ItemTemplate. And try an ItemsControl Outer Object instead of listview

<ItemsControl  IsEnabled="{Binding GameBoardEnabled}" 
                x:Name="_board" 
                ItemsSource ="{Binding Board}" 
                ItemTemplate= "{DynamicResource GamePieceTemplate}"  >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <!--<StackPanel/>-->
                        <UniformGrid                          
                            Columns="{Binding Columns}" 
                            Rows   ="{Binding Rows}"  />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
Mark Wardell
  • 493
  • 7
  • 25
  • Unless the SO wants item selection and keyboard navigation etc in which case ListView is probably correct . – Mark Feldman Mar 04 '16 at 02:04
  • UniformGrids always have to Have Rows+Columns I am pretty sure. So that is probably most of the answer. – Mark Wardell Mar 04 '16 at 02:08
  • If you mean that the items have to specify the row and column number then no, that's only Grids. UniformGrids just take a regular collection and do the row/collumn layout automatically. And if it's in a ListView then keyboard arrow buttons will be interpreted corrected as well for changing the currently selected item. – Mark Feldman Mar 04 '16 at 02:19
  • You are Right Mark. No need for both the Rows/Cols. But there is Rows/Cols On a grid - but that is a side issue. – Mark Wardell Mar 04 '16 at 02:51
  • @MarkWardell Does the `UniformGrid` go inside the regular grid? Also, can you explain what the variables `Rows` and `Columns` and `GamePieceTemplate` are like? – Luke Dinkler Mar 04 '16 at 18:42
  • 1
    @Luke yes! Rows is a ViewModel Variable describing the Row Count in a UniformGrid. Similarly Columns performs the same task for Columns. I learned while answering your Question, and fm Mark Feldman's input, that you need only set one or the other. In your Case I say you need to decide in your Vm how many Columns you want. Also the GamePiece Template draws a Rectangle and Fills it with an Image: for a full example that uses this technique: http://www.codeproject.com/Articles/991634/Undo-Redo-Implemented-via-Stateless-Command-Stacks – Mark Wardell Mar 04 '16 at 21:45
  • here is the GamePieceTemplate: – Mark Wardell Mar 04 '16 at 21:54