c# - 如何在 C# 中以编程方式创建 WPF 可滚动 StackPanel 列表

标签 c# wpf listview dynamic

我的情况:

我正在开发一个 C# WPF 应用程序(在 Windows 上),我需要在其中动态地创建许多在运行时 的控件。由于应用程序的性质,我无法为我的 WPF 窗口的许多方面使用标准 XAML(带模板)。这是一个非常独特的案例,不,我不会重新考虑我的申请格式。

我想完成的事情:

我想以编程方式创建一个显示StackPanel(或任何其他有效控制组)的可滚动列表的控件,对于一个用例,每个由位于 TextBlock 控件(标题/标题)之上的 Image 控件(图片)组成:

  • 我宁愿在任何数据绑定(bind)的情况下完成这一切(推理见下文)。因为项目是在运行时定义的,所以我应该能够在没有它们的情况下通过迭代来完成此操作。
  • 控件/查看器应该能够有多个列/行,所以它不是一维的(像典型的 ListBox 控件)。
  • 它还应该可以互换,以便您可以修改(添加、删除等)控件中的项目。

我附上了一张图片(下方),为您提供了一个可能的用例示例。

past ,我已经能够通过使用 XAML 将 ListViewItemTemplate(包装在 ScrollViewer 中)一起使用来完成所有这一切。但是,如果完全使用 C# 代码执行此操作,则难度会更大一些。我最近用普通的 c# 代码(使用 FrameworkElementFactory)制作了 ControlTemplate。它可能会变得有点复杂,而且我不确定这是否真的是最佳实践。应该我尝试采用相同的方法(使用带模板的 ListView)?如果是这样,怎么做?或者是否有更简单、更优雅的选项来使用 C# 代码实现?

enter image description here

编辑:我真的希望使用任何数据绑定(bind)。我只想创建一个(可滚动的)StackPanel“列表”,我可以轻松修改/调整它。使用数据绑定(bind)感觉像是一种倒退的实现,违背了运行时动态特性的目的。

编辑 2 (1/25/2018): 没有太多回应。我只需要一个统一的、可滚动的堆栈面板列表。我可以调整它以满足我的需要,但它需要全部在 C# 中(代码隐藏)。如果有人需要更多信息/说明,请告诉我。谢谢。

LINK TO XAML POST

最佳答案

这是一种在代码中使用 ListBoxUniformGrid 作为 ItemsPanelTemplate 的方法。或者,您只能使用 UniformGrid 并将其放在 ScrollViewer 中,但由于 ListBox 已经处理了选择和所有这些事情,您可能最好坚持那个。此代码将根据可用宽度自动调整一行中的项目数。

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();
    }
}

电影项目.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); }
    }
}

主窗口.xaml:

<Grid>
    <local:MoviePresenter x:Name="moviePresenter" 
                          ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
</Grid>

主窗口.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);
        }
    }
}

关于c# - 如何在 C# 中以编程方式创建 WPF 可滚动 StackPanel 列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48311483/

相关文章:

c# - 是否可以调用特定的 msconfig 选项卡?

c# - 什么是 ??在我的属性(property)?

C# WPF 游戏键盘移动

WPF - 阻止列表框项目选择

c# - 动态创建控件时,RadioButton 上的已检查事件命令绑定(bind)不起作用

c# - EF Core - 用于简单 ValueObject 的 ValueConverter 或 OwnedType?

c# - 与一个以上的 child 接壤

android - ListView View 在滚动时随机更新

java - 在同一个Android Studio页面中显示两个listView

c# - 如何获取文件和文件夹的图标以像 Windows 资源管理器一样插入到 treeView 和 listView?