我的情况:
我正在开发一个C#WPF应用程序(在Windows上),我需要在运行时中动态创建很多控件。由于应用程序的性质,我无法在WPF窗口的许多方面使用标准XAML(带有模板)。这是一个非常独特的案例,不,我不会重新考虑我的申请格式。
我想要完成的任务:
我想以编程方式创建一个显示StackPanel
s(或任何其他有效控制组)的可滚动列表的控件,对于一个用例,每个控件组都包含一个Image
控件(标题/标题)顶部的TextBlock
控件(图片):
ListBox
控件一样)。我已经包含了一张图片(下方),为您提供了一个可能的用例示例。
在past中,我已经能够使用ListView
使用ItemTemplate
(包裹在ScrollViewer
中)使用XAML来完成所有这些操作。但是,完全使用C#代码执行此操作会使其更加困难。我最近使用普通的c#代码ControlTemplate
进行了FrameworkElementFactory
s。它可能会有点复杂,我不确定它是不是最好的做法。我应该尝试去做同样的事情。路由(使用ListView
和模板)?如果是这样,怎么样?或者是否有一个更简单,更优雅的选项来实现C#代码?
编辑:我真的更喜欢不来使用任何数据绑定。我只想创建一个StackPanels的(可滚动的)列表,我可以轻松地修改/调整它。使用数据绑定感觉就像是向后实现,并且违背了运行时动态特性的目的。
编辑2(1/25/2018):响应不多。我只需要一个统一的,可滚动的stackpanel列表。我可以调整它以满足我的需求,但它需要全部用C#(代码隐藏)。如果有人需要更多信息/澄清,请告诉我。感谢。
答案 0 :(得分:3)
以下是使用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();
}
}
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);
}
}
}