WPF自定义小型控件开发需求:数据库数据加载替代默认Grid控件
Got it, let's walk through building this custom WPF control and getting your data displayed exactly how you need it. I'll break this down into manageable steps so you can follow along easily.
Since you don't want to use the default WPF Grid, let's create a reusable CustomListItem control structured to hold Image, Title, Content, and Date (aligned to match typical card-style layouts like your referenced image):
<UserControl x:Class="YourNamespace.CustomListItem" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" d:DesignHeight="120" d:DesignWidth="400"> <Border BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Padding="8" Margin="4" Background="White"> <DockPanel> <Image x:Name="ItemImage" Width="80" Height="80" DockPanel.Dock="Left" Stretch="UniformToFill" Margin="0 0 8 0"/> <StackPanel> <TextBlock x:Name="ItemTitle" FontSize="16" FontWeight="Bold" Margin="0 0 0 4"/> <TextBlock x:Name="ItemContent" FontSize="14" Foreground="#666" TextWrapping="Wrap" MaxHeight="40"/> <TextBlock x:Name="ItemDate" FontSize="12" Foreground="#999" HorizontalAlignment="Right" Margin="0 4 0 0"/> </StackPanel> </DockPanel> </Border> </UserControl>
To make setting Title, Content, Image, and Date as straightforward as your code example, add dependency properties to the control's code-behind. This lets you set values directly in code or via data binding later:
using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; namespace YourNamespace { public partial class CustomListItem : UserControl { public CustomListItem() { InitializeComponent(); } // Title Property public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(CustomListItem), new PropertyMetadata(string.Empty, (d, e) => ((CustomListItem)d).ItemTitle.Text = e.NewValue.ToString())); public string Title { get => (string)GetValue(TitleProperty); set => SetValue(TitleProperty, value); } // Content Property public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(string), typeof(CustomListItem), new PropertyMetadata(string.Empty, (d, e) => ((CustomListItem)d).ItemContent.Text = e.NewValue.ToString())); public string Content { get => (string)GetValue(ContentProperty); set => SetValue(ContentProperty, value); } // Image Property public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(nameof(Image), typeof(BitmapImage), typeof(CustomListItem), new PropertyMetadata(null, (d, e) => ((CustomListItem)d).ItemImage.Source = e.NewValue as BitmapImage)); public BitmapImage Image { get => (BitmapImage)GetValue(ImageProperty); set => SetValue(ImageProperty, value); } // Date Property public static readonly DependencyProperty DateProperty = DependencyProperty.Register(nameof(Date), typeof(DateTime), typeof(CustomListItem), new PropertyMetadata(DateTime.Now, (d, e) => ((CustomListItem)d).ItemDate.Text = ((DateTime)e.NewValue).ToString("MM/dd/yyyy"))); public DateTime Date { get => (DateTime)GetValue(DateProperty); set => SetValue(DateProperty, value); } } }
In your main window (or parent control), add a container to hold all CustomListItem instances. A StackPanel inside a ScrollViewer works great for a vertical list:
<Window x:Class="YourNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:YourNamespace" Title="Custom Data Display" Height="450" Width="800"> <ScrollViewer> <StackPanel x:Name="ItemsContainer" Margin="10"/> </ScrollViewer> </Window>
Adjust your provided foreach loop to create a new CustomListItem for each data element and add it to the container. Here's the modified code that works with our control:
// First, load your data from the database (replace with your actual fetch logic) var data = LoadDataFromDatabase(); // Populate the custom control foreach (var element in data) { var newItem = new CustomListItem(); newItem.Title = element.Title; newItem.Content = element.Content; // Convert your database image (e.g., byte array) to a BitmapImage newItem.Image = ConvertDbImageToBitmap(element.Image); newItem.Date = element.Date; ItemsContainer.Children.Add(newItem); }
Helper method to convert a database-stored byte array to BitmapImage:
private BitmapImage ConvertDbImageToBitmap(byte[] imageBytes) { if (imageBytes == null || imageBytes.Length == 0) return null; using (var ms = new MemoryStream(imageBytes)) { var image = new BitmapImage(); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = ms; image.EndInit(); return image; } }
Here's a simplified snippet using Entity Framework Core to fetch data (adjust for your DB provider and model):
private List<YourDataModel> LoadDataFromDatabase() { using (var dbContext = new YourDbContext()) { return dbContext.YourDataTable.ToList(); } }
Make sure your YourDataModel class matches your database table structure (with Title, Content, Image, and Date properties).
内容的提问来源于stack exchange,提问作者user9107638

