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

WPF动态ListView实现:点击按钮切换SQL查询结果展示

实现WPF ListView动态切换列与数据(基于SQL Server查询)

没问题,这个需求我经常遇到,核心就是动态更新ListView的列定义和绑定的数据源,结合SQL查询的切换就能实现。下面给你一套完整的可落地方案,分步骤拆解:

1. XAML布局:搭建基础页面结构

首先我们需要在XAML里定义ListView和切换按钮,这里关键是让ListView的列能动态绑定到ViewModel的列集合——因为默认GridView不支持直接绑定列集合,所以我们需要写一个附加属性来实现这个能力。

<Window x:Class="DynamicListViewDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DynamicListViewDemo"
        Title="动态ListView演示" Height="450" Width="800">
    <Grid Margin="10">
        <!-- 切换按钮 -->
        <StackPanel Orientation="Horizontal" Margin="0 0 0 10">
            <Button Content="查看用户" Command="{Binding SwitchToUsersCommand}" Margin="0 0 10 0" Padding="8 4"/>
            <Button Content="查看项目" Command="{Binding SwitchToProjectsCommand}" Padding="8 4"/>
        </StackPanel>
        
        <!-- 动态列的ListView -->
        <ListView ItemsSource="{Binding CurrentData}" Margin="0 40 0 0">
            <ListView.View>
                <GridView local:GridViewHelper.ColumnsSource="{Binding CurrentColumns}"/>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

实现动态列的附加属性

这个附加属性的作用是把ViewModel里的列集合同步到GridView的Columns中,实现列的动态更新:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace DynamicListViewDemo
{
    public static class GridViewHelper
    {
        public static readonly DependencyProperty ColumnsSourceProperty =
            DependencyProperty.RegisterAttached(
                "ColumnsSource", 
                typeof(IEnumerable<GridViewColumn>), 
                typeof(GridViewHelper),
                new PropertyMetadata(null, OnColumnsSourceChanged));

        public static void SetColumnsSource(DependencyObject element, IEnumerable<GridViewColumn> value)
        {
            element.SetValue(ColumnsSourceProperty, value);
        }

        public static IEnumerable<GridViewColumn> GetColumnsSource(DependencyObject element)
        {
            return (IEnumerable<GridViewColumn>)element.GetValue(ColumnsSourceProperty);
        }

        private static void OnColumnsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is GridView gridView)
            {
                gridView.Columns.Clear();
                if (e.NewValue is IEnumerable<GridViewColumn> columns)
                {
                    foreach (var column in columns)
                    {
                        gridView.Columns.Add(column);
                    }
                }
            }
        }
    }
}

2. ViewModel:处理业务逻辑与数据绑定

我们用MVVM模式来分离UI和业务逻辑,实现INotifyPropertyChanged接口来通知UI更新,同时用命令来处理按钮点击事件:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;

namespace DynamicListViewDemo
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<object> _currentData;
        private IEnumerable<GridViewColumn> _currentColumns;
        private readonly DataAccess _dataAccess;

        // 按钮命令
        public ICommand SwitchToUsersCommand { get; }
        public ICommand SwitchToProjectsCommand { get; }

        // 当前绑定的数据源
        public ObservableCollection<object> CurrentData
        {
            get => _currentData;
            set
            {
                _currentData = value;
                OnPropertyChanged(nameof(CurrentData));
            }
        }

        // 当前的列集合
        public IEnumerable<GridViewColumn> CurrentColumns
        {
            get => _currentColumns;
            set
            {
                _currentColumns = value;
                OnPropertyChanged(nameof(CurrentColumns));
            }
        }

        public MainViewModel()
        {
            _dataAccess = new DataAccess();
            SwitchToUsersCommand = new RelayCommand(LoadUserData);
            SwitchToProjectsCommand = new RelayCommand(LoadProjectData);

            // 默认加载用户数据
            LoadUserData();
        }

        // 加载用户数据并切换列
        private void LoadUserData()
        {
            // 定义用户列表的列
            CurrentColumns = new List<GridViewColumn>
            {
                new GridViewColumn { Header = "First Name", DisplayMemberBinding = new Binding("FirstName") },
                new GridViewColumn { Header = "Last Name", DisplayMemberBinding = new Binding("LastName") },
                new GridViewColumn { Header = "Email", DisplayMemberBinding = new Binding("Email") }
            };

            // 从SQL Server获取用户数据
            var users = _dataAccess.GetUsers();
            CurrentData = new ObservableCollection<object>(users);
        }

        // 加载项目数据并切换列
        private void LoadProjectData()
        {
            // 定义项目列表的列
            CurrentColumns = new List<GridViewColumn>
            {
                new GridViewColumn { Header = "Project Name", DisplayMemberBinding = new Binding("ProjectName") },
                new GridViewColumn { Header = "Description", DisplayMemberBinding = new Binding("Description") }
            };

            // 从SQL Server获取项目数据
            var projects = _dataAccess.GetProjects();
            CurrentData = new ObservableCollection<object>(projects);
        }

        // 实现INotifyPropertyChanged接口
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    // 通用的RelayCommand实现,用于绑定按钮命令
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }

        public RelayCommand(Action execute, Func<bool> canExecute = null)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
        public void Execute(object parameter) => _execute();
    }
}

3. 数据访问层:从SQL Server获取数据

这里我们用Dapper来简化SQL查询(需要通过NuGet安装Dapper包),如果不想用第三方库,也可以用原生ADO.NET实现:

using System.Collections.Generic;
using System.Data.SqlClient;
using Dapper;

namespace DynamicListViewDemo
{
    public class DataAccess
    {
        // 替换成你的SQL Server连接字符串
        private readonly string _connectionString = "Server=localhost;Database=YourDB;Trusted_Connection=True;";

        // 查询用户数据
        public IEnumerable<User> GetUsers()
        {
            using (var conn = new SqlConnection(_connectionString))
            {
                conn.Open();
                // 这里可以根据实际表结构调整SQL语句
                return conn.Query<User>("SELECT FirstName, LastName, Email FROM Users");
            }
        }

        // 查询项目数据
        public IEnumerable<Project> GetProjects()
        {
            using (var conn = new SqlConnection(_connectionString))
            {
                conn.Open();
                // 根据实际表结构调整SQL语句
                return conn.Query<Project>("SELECT ProjectName, Description FROM Projects");
            }
        }
    }

    // 用户实体类,对应数据库Users表的字段
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }

    // 项目实体类,对应数据库Projects表的字段
    public class Project
    {
        public string ProjectName { get; set; }
        public string Description { get; set; }
    }
}

4. 绑定ViewModel到View

最后在MainWindow的构造函数里设置DataContext,让UI和ViewModel关联起来:

public MainWindow()
{
    InitializeComponent();
    DataContext = new MainViewModel();
}

一些额外建议

  • 如果数据量较大,建议把数据查询改成异步方式(用async/await),避免UI卡顿;
  • 可以把列定义放到XAML资源文件中,或者用DataTemplate来实现更复杂的列样式;
  • 如果查询有参数,一定要用参数化查询,防止SQL注入;
  • 可以添加加载状态提示(比如ProgressBar),提升用户体验。

内容的提问来源于stack exchange,提问作者Eric Obermuller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:42:21