定义产品型号:
public sealed class Product
{
public string Name { get; set; }
public string Description { get; set; }
public byte[] Image { get; set; }
}
定义服务以从数据库加载产品:
public sealed class ProductsService
{
public async Task<IEnumerable<Product>> LoadProductsAsync()
{
// TODO: to load products from database use your favorite ORM
// or raw ADO .NET; anyway, you want this to be async
// since this is I/O bound operation
}
}
定义产品视图模型。它只是包装模型并提供“添加到购物车”命令:
public sealed class ProductVm : ViewModelBase
{
private readonly Product model;
private readonly Lazy<ImageSource> image;
public ProductVm(Product model)
{
this.model = model;
// we need to load image just once, hence here comes Lazy<T>
image = new Lazy<ImageSource>(LoadImage);
// TODO: add command initialization here;
// usually you want DelegateCommand/RelayCommand/etc
// AddToCartCommand = new DelegateCommand(AddToCart);
}
public string Name => model.Name;
public string Description => model.Description;
public ImageSource Image => image.Value;
public ICommand AddToCartCommand { get; }
private ImageSource LoadImage()
{
if (model.Image == null)
{
return null;
}
var image = new BitmapImage();
using (var mem = new MemoryStream(model.Image))
{
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
}
定义主窗口视图模型:
public sealed class MainWindowVm
{
private readonly ProductsService productsService;
private ObservableCollection<ProductVm> products;
public MainWindowVm()
{
// in production apps, services must be injected using DI-containers
// like Autofac, MEF, NInject, etc
productsService = new ProductsService();
}
public IEnumerable<ProductVm> Products
{
get
{
if (products == null)
{
// when UI wants to display products,
// we create empty collection and initiate async operation;
// later, when async operation will be finished,
// we will populate collection using values from database
products = new ObservableCollection<ProductVm>();
var _ = LoadProductsAsync();
}
return products;
}
}
private async Task LoadProductsAsync()
{
// service returns collection of product models,
// but we need collection of product view models
var productModels = await productsService.LoadProductsAsync();
foreach (var model in productModels)
{
products.Add(new ProductVm(model));
}
}
}
定义一个视图。你需要ItemsControl、ItemTemplate和WrapPanel:
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowVm />
</Window.DataContext>
<ItemsControl ItemsSource="{Binding Products}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ProductVm}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Width="48" Height="48" Source="{Binding Image}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Description}"/>
<Button Grid.Row="2" Grid.Column="1" Content="Add to cart" HorizontalAlignment="Right" Command="{Binding AddToCartCommand}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
结果:
示例项目是here。