You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:18:45