How to programmatically create a scrollable StackPanel WPF list in C #

My situation:

I am developing a WPF application in C # (on Windows) where I need to dynamically create many of my controls at runtime. Due to the nature of the application, I cannot use standard XAML (with templates) for many aspects of my WPF windows. This is a very unique case, and no, I will not revise the format of my application.

What I want to do:

I would like to programmatically create a control that displays a StackPanel scrollable list (or any other effective control group), which in one case will consist of Image (image) on top of the TextBlock control (title / caption):

  • I would prefer to do all this without data binding (see below for reasoning). Since elements are determined at runtime, I have to do it without them using iteration.
  • The control / viewer must have several columns / rows, so it is not one-dimensional (for example, a typical ListBox control).
  • It must also be interchangeable so that you can modify (add, delete, etc.) the elements in the control.

I have included the picture (below) to give you an example of a possible use case.

In the past, I was able to accomplish all this using a ListView with an ItemTemplate (wrapped in a ScrollViewer ) using ScrollViewer , doing it completely with C # code makes it a little harder. I recently made a ControlTemplate in simple C # code (with FrameworkElementFactory This may get a little complicated and I'm not sure if this is really best practice. Should I try to go the same route (using ListView with a template)? If so, then how? Or is there a simpler, more elegant option for implementation with C # code?

enter image description here

Edit: I would prefer not to use any data bindings. I just want to create a (scrollable) StackPanels β€œlist” that I can easily modify / customize. Using data bindings looks like a reverse implementation and hits the target of the dynamic nature of the runtime.

Edit 2 (1/25/2018): Not many answers. I just need a single, scrollable list of stack panels . I can customize it according to my needs, but it should all be in C # (back-code). If anyone needs more information / clarification, please let me know. Thanks.

XAML MAIL LINK

+7
c # dynamic listview wpf
source share
1 answer

Here you can do this in code using a ListBox with a UniformGrid as ItemsPanelTemplate . In addition, you can only use UniformGrid and place it inside a ScrollViewer , but since the ListBox already handles the selection and all that, you probably stick to it better. This code will automatically adjust the number of elements per line 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); } } } 
+3
source share

All Articles