我的情况:
我正在开发一个 C# WPF 应用程序(在 Windows 上),我需要在其中动态地创建许多在运行时 的控件。由于应用程序的性质,我无法为我的 WPF 窗口的许多方面使用标准 XAML(带模板)。这是一个非常独特的案例,不,我不会重新考虑我的申请格式。
我想完成的事情:
我想以编程方式创建一个显示StackPanel
(或任何其他有效控制组)的可滚动列表的控件,对于一个用例,每个由位于 TextBlock
控件(标题/标题)之上的 Image
控件(图片)组成:
- 我宁愿在不任何数据绑定(bind)的情况下完成这一切(推理见下文)。因为项目是在运行时定义的,所以我应该能够在没有它们的情况下通过迭代来完成此操作。
- 控件/查看器应该能够有多个列/行,所以它不是一维的(像典型的
ListBox
控件)。 - 它还应该可以互换,以便您可以修改(添加、删除等)控件中的项目。
我附上了一张图片(下方),为您提供了一个可能的用例示例。
在past ,我已经能够通过使用 XAML 将 ListView
与 ItemTemplate
(包装在 ScrollViewer
中)一起使用来完成所有这一切。但是,如果完全使用 C# 代码执行此操作,则难度会更大一些。我最近用普通的 c# 代码(使用 FrameworkElementFactory
)制作了 ControlTemplate
。它可能会变得有点复杂,而且我不确定这是否真的是最佳实践。应该我尝试采用相同的方法(使用带模板的 ListView
)?如果是这样,怎么做?或者是否有更简单、更优雅的选项来使用 C# 代码实现?
编辑:我真的希望不使用任何数据绑定(bind)。我只想创建一个(可滚动的)StackPanel“列表”,我可以轻松修改/调整它。使用数据绑定(bind)感觉像是一种倒退的实现,违背了运行时动态特性的目的。
编辑 2 (1/25/2018): 没有太多回应。我只需要一个统一的、可滚动的堆栈面板列表。我可以调整它以满足我的需要,但它需要全部在 C# 中(代码隐藏)。如果有人需要更多信息/说明,请告诉我。谢谢。
最佳答案
这是一种在代码中使用 ListBox
和 UniformGrid
作为 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/