如何在WPF中创建动态网格?基于泛型列表生成行列并动态加载数据
在WPF中创建基于泛型列表的动态网格
嘿,我来帮你搞定WPF里的动态网格问题!根据你给出的RestaurantMenu和RestaurantSubMenu模型,咱们可以通过数据绑定+模板控件的组合,轻松实现基于泛型列表的动态行和列,完美适配你那种侧边按钮切换数据的场景。
一、核心实现思路
- 用
ItemsControl(或ListView)作为主容器,绑定你的RestaurantMenu泛型列表,自动生成每一行 - 嵌套子控件绑定
SubMenus列表,动态生成每行的子项(也就是你说的“列”) - 借助WPF的自动更新特性,当数据源变化时,UI会同步刷新
二、完整XAML示例
假设你的窗口/用户控件绑定了一个包含ObservableCollection<RestaurantMenu>的ViewModel(一定要用ObservableCollection,普通List无法自动触发UI更新):
<Window x:Class="WpfDynamicGrid.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="动态菜单网格" Height="450" Width="800"> <Grid> <!-- 侧边菜单按钮区 --> <StackPanel Width="150" VerticalAlignment="Stretch" Background="#F5F5F5"> <Button Content="菜单组1" Click="MenuBtn_Click" Tag="1" Margin="5"/> <Button Content="菜单组2" Click="MenuBtn_Click" Tag="2" Margin="5"/> <!-- 更多按钮可根据需求动态生成 --> </StackPanel> <!-- 动态网格展示区 --> <ItemsControl Margin="160,10,10,10" ItemsSource="{Binding CurrentMenus}"> <!-- 定义每一行(单个RestaurantMenu)的模板 --> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Margin="0,5"> <Grid.ColumnDefinitions> <ColumnDefinition Width="160"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 菜单名称列 --> <TextBlock Grid.Column="0" Text="{Binding MenuName}" FontSize="14" FontWeight="Bold" VerticalAlignment="Center" Padding="5"/> <!-- 子菜单动态列:用WrapPanel自动换行,也可替换为UniformGrid固定列数 --> <WrapPanel Grid.Column="1" Orientation="Horizontal" ItemsSource="{Binding SubMenus}"> <WrapPanel.ItemTemplate> <DataTemplate> <!-- 子菜单样式,匹配你示例中的蓝色数据项 --> <TextBlock Text="{Binding SubMenuName}" Background="#3399FF" Foreground="White" Padding="10,5" Margin="3,0" VerticalAlignment="Center" CornerRadius="3"/> </DataTemplate> </WrapPanel.ItemTemplate> </WrapPanel> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> <!-- 行分隔线样式 --> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Border BorderThickness="0,0,0,1" BorderBrush="#E0E0E0"> <ContentPresenter/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl> </Grid> </Window>
三、后台代码与ViewModel实现
首先补全你的模型类(假设RestaurantSubMenu包含显示用的SubMenuName属性):
public class RestaurantMenu { public int MenuId { get; set; } public string MenuName { get; set; } public IList<RestaurantSubMenu> SubMenus { get; set; } } public class RestaurantSubMenu { public int SubMenuId { get; set; } public string SubMenuName { get; set; } // 其他业务属性按需添加 }
然后创建ViewModel(实现INotifyPropertyChanged,确保属性变化时UI同步更新):
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; public class MainViewModel : INotifyPropertyChanged { private ObservableCollection<RestaurantMenu> _currentMenus; public ObservableCollection<RestaurantMenu> CurrentMenus { get => _currentMenus; set { _currentMenus = value; OnPropertyChanged(); } } // 模拟从数据库获取数据的方法,替换为你的实际查询逻辑 public ObservableCollection<RestaurantMenu> LoadMenuData(int menuGroupId) { return new ObservableCollection<RestaurantMenu> { new RestaurantMenu { MenuId = 1, MenuName = "主食类", SubMenus = new List<RestaurantSubMenu> { new RestaurantSubMenu { SubMenuId = 101, SubMenuName = "牛肉炒饭" }, new RestaurantSubMenu { SubMenuId = 102, SubMenuName = "日式拉面" }, new RestaurantSubMenu { SubMenuId = 103, SubMenuName = "芝士汉堡" } } }, new RestaurantMenu { MenuId = 2, MenuName = "饮品类", SubMenus = new List<RestaurantSubMenu> { new RestaurantSubMenu { SubMenuId = 201, SubMenuName = "冰可乐" }, new RestaurantSubMenu { SubMenuId = 202, SubMenuName = "珍珠奶茶" } } } }; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
最后在窗口后台初始化ViewModel并处理按钮点击:
public partial class MainWindow : Window { private MainViewModel _viewModel; public MainWindow() { InitializeComponent(); _viewModel = new MainViewModel(); DataContext = _viewModel; // 加载默认数据 _viewModel.CurrentMenus = _viewModel.LoadMenuData(1); } private void MenuBtn_Click(object sender, RoutedEventArgs e) { if (sender is Button btn && int.TryParse(btn.Tag.ToString(), out int groupId)) { // 切换数据源,UI会自动刷新 _viewModel.CurrentMenus = _viewModel.LoadMenuData(groupId); } } }
四、关键注意事项
- 必须用
ObservableCollection:普通List无法触发UI自动更新,ObservableCollection实现了集合变更通知接口 - 子项布局灵活切换:示例用
WrapPanel实现自动换行,若需要固定列数可替换为UniformGrid;若要表格式列可嵌套GridView - 样式定制:可通过修改
DataTemplate和ItemContainerStyle,完全匹配你示例中的蓝色数据项样式
内容的提问来源于stack exchange,提问作者Kshitij Rodrigues
相关产品推荐
相关产品推荐

