Here's a way to do it in code using a ListBox
with UniformGrid
as ItemsPanelTemplate
. Alternatively, you can only use a UniformGrid
and put it inside a ScrollViewer
, but as the ListBox
already handles selection and all that stuff, you probably better stick with that one. This code will automatically adjust the number of items in a row depending on the available width.
MoviePresenter.cs :
public class MoviePresenter : ListBox
{
public MoviePresenter()
{
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(UniformGrid));
factory.SetBinding(
UniformGrid.ColumnsProperty,
new Binding(nameof(ActualWidth))
{
Source = this,
Mode = BindingMode.OneWay,
Converter = new WidthToColumnsConverter()
{
ItemMinWidth = 100
}
});
ItemsPanel = new ItemsPanelTemplate()
{
VisualTree = factory
};
}
}
internal class WidthToColumnsConverter : IValueConverter
{
public double ItemMinWidth { get; set; } = 1;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double? actualWidth = value as double?;
if (!actualWidth.HasValue)
return Binding.DoNothing;
return Math.Max(1, Math.Floor(actualWidth.Value / ItemMinWidth));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
MovieItem.cs :
public class MovieItem : Grid
{
public MovieItem()
{
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
Image image = new Image();
image.Stretch = Stretch.UniformToFill;
image.SetBinding(Image.SourceProperty, new Binding(nameof(ImageSource)) { Source = this });
Children.Add(image);
TextBlock title = new TextBlock();
title.FontSize += 1;
title.FontWeight = FontWeights.Bold;
title.Foreground = Brushes.Beige;
title.TextTrimming = TextTrimming.CharacterEllipsis;
title.SetBinding(TextBlock.TextProperty, new Binding(nameof(Title)) { Source = this });
Grid.SetRow(title, 1);
Children.Add(title);
TextBlock year = new TextBlock();
year.Foreground = Brushes.LightGray;
year.TextTrimming = TextTrimming.CharacterEllipsis;
year.SetBinding(TextBlock.TextProperty, new Binding(nameof(Year)) { Source = this });
Grid.SetRow(year, 2);
Children.Add(year);
TextBlock releaseDate = new TextBlock();
releaseDate.Foreground = Brushes.LightGray;
releaseDate.TextTrimming = TextTrimming.CharacterEllipsis;
releaseDate.SetBinding(TextBlock.TextProperty, new Binding(nameof(ReleaseDate)) { Source = this });
Grid.SetRow(releaseDate, 3);
Children.Add(releaseDate);
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public static readonly DependencyProperty YearProperty =
DependencyProperty.Register("Year", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public static readonly DependencyProperty ReleaseDateProperty =
DependencyProperty.Register("ReleaseDate", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public string ImageSource
{
get { return (string)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public string Year
{
get { return (string)GetValue(YearProperty); }
set { SetValue(YearProperty, value); }
}
public string ReleaseDate
{
get { return (string)GetValue(ReleaseDateProperty); }
set { SetValue(ReleaseDateProperty, value); }
}
}
MainWindow.xaml :
<Grid>
<local:MoviePresenter x:Name="moviePresenter"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 20; i++)
{
DateTime dummyDate = DateTime.Now.AddMonths(-i).AddDays(-(i * i));
MovieItem item = new MovieItem()
{
ImageSource = $"http://fakeimg.pl/100x200/?text=Image_{i}",
Title = $"Dummy movie {i}",
Year = $"{dummyDate.Year}",
ReleaseDate = $"{dummyDate.ToLongDateString()}"
};
moviePresenter.Items.Add(item);
}
}
}