你刚从传统的WinForm DataGridView转向WPF的DataGrid,满怀期待地加载了10万条数据,结果界面直接卡死30秒,用户体验瞬间崩塌,事实上在Winform下10万条其实也最好用虚数据加载或分页了。
本文将带你彻底解决WPF DataGrid的大数据分页与筛选难题,让你的应用从"卡顿王"变成"性能王"。
WPF的数据绑定机制虽然强大,但也带来了性能挑战:
C#// WinForm 传统做法(性能较好)
dataGridView1.DataSource = dataTable; // 直接绑定,这块Winform还是可以的
// WPF 错误做法(性能灾难)
dataGrid.ItemsSource = database.GetAllRecords(); // 一次性加载所有数据
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace AppDataGridPage
{
// 分页请求模型
public class PageRequest
{
public int PageIndex { get; set; } = 1;
public int PageSize { get; set; } = 50;
public string SearchText { get; set; } = "";
public string SortColumn { get; set; } = "";
public bool IsAscending { get; set; } = true;
}
// 分页结果模型
public class PageResult<T>
{
public List<T> Data { get; set; }
public int TotalCount { get; set; }
public int PageCount { get; set; }
public int CurrentPage { get; set; }
}
// 业务数据模型
public class Employee : INotifyPropertyChanged
{
private string _name;
private string _department;
private decimal _salary;
public int Id { get; set; }
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public string Department
{
get => _department;
set
{
_department = value;
OnPropertyChanged();
}
}
public decimal Salary
{
get => _salary;
set
{
_salary = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
C#using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
namespace AppDataGridPage
{
public class EmployeeViewModel : INotifyPropertyChanged
{
private readonly EmployeeService _employeeService;
private ObservableCollection<Employee> _employees;
private int _currentPage = 1;
private int _pageSize = 50;
private int _totalCount;
private int _pageCount;
private string _searchText = "";
private bool _isLoading;
public EmployeeViewModel()
{
_employeeService = new EmployeeService();
Employees = new ObservableCollection<Employee>();
// 🎯 命令初始化
LoadDataCommand = new RelayCommand(async () => await LoadDataAsync());
SearchCommand = new RelayCommand(async () => await SearchAsync());
FirstPageCommand = new RelayCommand(async () => await GoToPageAsync(1));
PreviousPageCommand = new RelayCommand(async () => await GoToPageAsync(CurrentPage - 1));
NextPageCommand = new RelayCommand(async () => await GoToPageAsync(CurrentPage + 1));
LastPageCommand = new RelayCommand(async () => await GoToPageAsync(PageCount));
// 初始加载
_ = LoadDataAsync();
}
#region 属性
public ObservableCollection<Employee> Employees
{
get => _employees;
set
{
_employees = value;
OnPropertyChanged();
}
}
public int CurrentPage
{
get => _currentPage;
set
{
_currentPage = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PageInfo));
}
}
public int PageSize
{
get => _pageSize;
set
{
_pageSize = value;
OnPropertyChanged();
}
}
public int TotalCount
{
get => _totalCount;
set
{
_totalCount = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PageInfo));
}
}
public int PageCount
{
get => _pageCount;
set
{
_pageCount = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PageInfo));
}
}
public string SearchText
{
get => _searchText;
set
{
_searchText = value;
OnPropertyChanged();
}
}
public bool IsLoading
{
get => _isLoading;
set
{
_isLoading = value;
OnPropertyChanged();
}
}
public string PageInfo => $"第 {CurrentPage} 页,共 {PageCount} 页,总计 {TotalCount} 条记录";
#endregion
#region 命令
public ICommand LoadDataCommand { get; }
public ICommand SearchCommand { get; }
public ICommand FirstPageCommand { get; }
public ICommand PreviousPageCommand { get; }
public ICommand NextPageCommand { get; }
public ICommand LastPageCommand { get; }
#endregion
#region 方法
/// <summary>
/// 加载数据 - 核心方法
/// </summary>
private async Task LoadDataAsync()
{
IsLoading = true;
try
{
var request = new PageRequest
{
PageIndex = CurrentPage,
PageSize = PageSize,
SearchText = SearchText
};
var result = await _employeeService.GetPagedEmployeesAsync(request);
// 🔄 UI线程更新
Application.Current.Dispatcher.Invoke(() =>
{
Employees.Clear();
foreach (var employee in result.Data)
{
Employees.Add(employee);
}
TotalCount = result.TotalCount;
PageCount = result.PageCount;
CurrentPage = result.CurrentPage;
});
}
catch (Exception ex)
{
MessageBox.Show($"加载数据失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
IsLoading = false;
}
}
/// <summary>
/// 搜索功能
/// </summary>
private async Task SearchAsync()
{
CurrentPage = 1; // 搜索时重置到第一页
await LoadDataAsync();
}
/// <summary>
/// 跳转到指定页
/// </summary>
private async Task GoToPageAsync(int page)
{
if (page < 1 || page > PageCount) return;
CurrentPage = page;
await LoadDataAsync();
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// 简单的Command实现
public class RelayCommand : ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Func<Task> execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public async void Execute(object parameter) => await _execute();
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
}
C#using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows;
namespace AppDataGridPage
{
/// <summary>
/// 布尔值到可见性转换器
/// </summary>
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
// 支持反向转换:参数为"Inverse"时,true显示为Hidden/Collapsed
bool inverse = parameter?.ToString() == "Inverse";
if (inverse)
{
return boolValue ? Visibility.Collapsed : Visibility.Visible;
}
else
{
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility visibility)
{
bool inverse = parameter?.ToString() == "Inverse";
if (inverse)
{
return visibility != Visibility.Visible;
}
else
{
return visibility == Visibility.Visible;
}
}
return false;
}
}
/// <summary>
/// 字符串空值到可见性转换器
/// </summary>
public class StringNullOrEmptyToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isEmpty = string.IsNullOrEmpty(value?.ToString());
bool inverse = parameter?.ToString() == "Inverse";
if (inverse)
{
return isEmpty ? Visibility.Visible : Visibility.Collapsed;
}
else
{
return isEmpty ? Visibility.Collapsed : Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
C#public class EmployeeViewModel : INotifyPropertyChanged
{
private readonly EmployeeService _employeeService;
private ObservableCollection<Employee> _employees;
private int _currentPage = 1;
private int _pageSize = 50;
private int _totalCount;
private int _pageCount;
private string _searchText = "";
private bool _isLoading;
public EmployeeViewModel()
{
_employeeService = new EmployeeService();
Employees = new ObservableCollection<Employee>();
// 🎯 命令初始化
LoadDataCommand = new RelayCommand(async () => await LoadDataAsync());
SearchCommand = new RelayCommand(async () => await SearchAsync());
FirstPageCommand = new RelayCommand(async () => await GoToPageAsync(1));
PreviousPageCommand = new RelayCommand(async () => await GoToPageAsync(CurrentPage - 1));
NextPageCommand = new RelayCommand(async () => await GoToPageAsync(CurrentPage + 1));
LastPageCommand = new RelayCommand(async () => await GoToPageAsync(PageCount));
// 初始加载
_ = LoadDataAsync();
}
#region 属性
public ObservableCollection<Employee> Employees
{
get => _employees;
set
{
_employees = value;
OnPropertyChanged();
}
}
public int CurrentPage
{
get => _currentPage;
set
{
_currentPage = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PageInfo));
}
}
public int PageSize
{
get => _pageSize;
set
{
_pageSize = value;
OnPropertyChanged();
}
}
public int TotalCount
{
get => _totalCount;
set
{
_totalCount = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PageInfo));
}
}
public int PageCount
{
get => _pageCount;
set
{
_pageCount = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PageInfo));
}
}
public string SearchText
{
get => _searchText;
set
{
_searchText = value;
OnPropertyChanged();
}
}
public bool IsLoading
{
get => _isLoading;
set
{
_isLoading = value;
OnPropertyChanged();
}
}
public string PageInfo => $"第 {CurrentPage} 页,共 {PageCount} 页,总计 {TotalCount} 条记录";
#endregion
#region 命令
public ICommand LoadDataCommand { get; }
public ICommand SearchCommand { get; }
public ICommand FirstPageCommand { get; }
public ICommand PreviousPageCommand { get; }
public ICommand NextPageCommand { get; }
public ICommand LastPageCommand { get; }
#endregion
#region 方法
/// <summary>
/// 加载数据 - 核心方法
/// </summary>
private async Task LoadDataAsync()
{
IsLoading = true;
try
{
var request = new PageRequest
{
PageIndex = CurrentPage,
PageSize = PageSize,
SearchText = SearchText
};
var result = await _employeeService.GetPagedEmployeesAsync(request);
// 🔄 UI线程更新
Application.Current.Dispatcher.Invoke(() =>
{
Employees.Clear();
foreach (var employee in result.Data)
{
Employees.Add(employee);
}
TotalCount = result.TotalCount;
PageCount = result.PageCount;
CurrentPage = result.CurrentPage;
});
}
catch (Exception ex)
{
MessageBox.Show($"加载数据失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
IsLoading = false;
}
}
/// <summary>
/// 搜索功能
/// </summary>
private async Task SearchAsync()
{
CurrentPage = 1; // 搜索时重置到第一页
await LoadDataAsync();
}
/// <summary>
/// 跳转到指定页
/// </summary>
private async Task GoToPageAsync(int page)
{
if (page < 1 || page > PageCount) return;
CurrentPage = page;
await LoadDataAsync();
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// 简单的Command实现
public class RelayCommand : ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Func<Task> execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public async void Execute(object parameter) => await _execute();
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
XML<Window x:Class="AppDataGridPage.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:AppDataGridPage"
mc:Ignorable="d"
Title="员工管理系统" Height="550" Width="900"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResize"
Background="#F5F5F5">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<!-- 🎨 现代化按钮样式 -->
<Style x:Key="ModernButton" TargetType="Button">
<Setter Property="Background" Value="#2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1976D2"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#0D47A1"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#BDBDBD"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 🔍 搜索按钮样式 -->
<Style x:Key="SearchButton" TargetType="Button" BasedOn="{StaticResource ModernButton}">
<Setter Property="Background" Value="#4CAF50"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#388E3C"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#2E7D32"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 🔄 重置按钮样式 -->
<Style x:Key="ResetButton" TargetType="Button" BasedOn="{StaticResource ModernButton}">
<Setter Property="Background" Value="#FF9800"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#F57C00"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#E65100"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 📄 分页按钮样式 -->
<Style x:Key="PaginationButton" TargetType="Button" BasedOn="{StaticResource ModernButton}">
<Setter Property="Background" Value="#607D8B"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#455A64"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#263238"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 🎯 现代化文本框样式 -->
<Style x:Key="ModernTextBox" TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#E0E0E0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost"
Padding="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="#2196F3"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#BDBDBD"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 📊 现代化DataGrid样式 -->
<Style x:Key="ModernDataGrid" TargetType="DataGrid">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#E0E0E0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
<Setter Property="HorizontalGridLinesBrush" Value="#F0F0F0"/>
<Setter Property="RowHeaderWidth" Value="0"/>
<Setter Property="FontSize" Value="12"/>
<!-- 🎯 重点:优化选中单元格样式 -->
<Setter Property="CellStyle">
<Setter.Value>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="10,8"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- 移除默认的选中单元格样式 -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Trigger>
<!-- 移除焦点样式 -->
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>
<!-- 📋 优化后的DataGrid行样式 -->
<Style x:Key="ModernDataGridRow" TargetType="DataGridRow">
<Setter Property="Height" Value="45"/>
<Setter Property="Background" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Margin" Value="0,1"/>
<Style.Triggers>
<!-- 🎯 悬停效果 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#F0F8FF" Offset="0"/>
<GradientStop Color="#E6F3FF" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="#2196F3" Opacity="0.3" BlurRadius="5" ShadowDepth="0"/>
</Setter.Value>
</Setter>
</Trigger>
<!-- 🌟 重点:优化选中行样式 -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#2196F3" Offset="0"/>
<GradientStop Color="#1976D2" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="#1976D2" Opacity="0.4" BlurRadius="8" ShadowDepth="2"/>
</Setter.Value>
</Setter>
</Trigger>
<!-- 🎨 交替行背景 -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=ItemsControl.AlternationIndex}" Value="1">
<Setter Property="Background" Value="#FAFAFA"/>
</DataTrigger>
<!-- 🎯 选中且悬停的组合效果 -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#1976D2" Offset="0"/>
<GradientStop Color="#0D47A1" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="#0D47A1" Opacity="0.5" BlurRadius="10" ShadowDepth="3"/>
</Setter.Value>
</Setter>
</MultiTrigger>
</Style.Triggers>
</Style>
<!-- 🎯 现代化DataGrid列头样式 -->
<Style x:Key="ModernDataGridColumnHeader" TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#F8F9FA"/>
<Setter Property="Foreground" Value="#495057"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Height" Value="40"/>
<Setter Property="BorderBrush" Value="#DEE2E6"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
<Setter Property="Padding" Value="12,0"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E9ECEF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 💫 加载动画样式 -->
<Style x:Key="LoadingText" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Foreground" Value="#2196F3"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.5" To="1" Duration="0:0:0.8"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<!-- 📊 页面信息样式 -->
<Style x:Key="PageInfoText" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Foreground" Value="#666666"/>
<Setter Property="Background" Value="White"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Opacity="0.1" BlurRadius="3" ShadowDepth="1"/>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<!-- 🌟 主容器 -->
<Border Background="White"
CornerRadius="8"
Margin="15"
>
<Border.Effect>
<DropShadowEffect Color="Black" Opacity="0.1" BlurRadius="10" ShadowDepth="2"/>
</Border.Effect>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 🏷️ 标题区域 -->
<TextBlock Grid.Row="0"
Text="员工信息管理"
FontSize="20"
FontWeight="Bold"
Foreground="#2196F3"
Margin="0,0,0,20"
HorizontalAlignment="Left"/>
<!-- 🔍 搜索区域 -->
<Border Grid.Row="1"
Background="#F9F9F9"
CornerRadius="6"
Padding="15"
Margin="0,0,0,15">
<StackPanel Orientation="Horizontal">
<TextBlock Text="🔍"
FontSize="16"
VerticalAlignment="Center"
Margin="0,0,8,0"/>
<TextBox x:Name="SearchTextBox"
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBox}"
Width="250"
Height="35"
VerticalContentAlignment="Center"
Margin="0,0,15,0"/>
<Button Content="搜索"
Command="{Binding SearchCommand}"
Style="{StaticResource SearchButton}"
Width="80"
Height="35"
Margin="0,0,10,0"/>
<Button Content="重置"
Command="{Binding LoadDataCommand}"
Style="{StaticResource ResetButton}"
Width="80"
Height="35"
Margin="0,0,20,0"/>
<!-- 💫 加载状态 -->
<StackPanel Orientation="Horizontal"
Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock Text="⏳"
FontSize="14"
VerticalAlignment="Center"
Margin="0,0,5,0"/>
<TextBlock Text="加载中..."
Style="{StaticResource LoadingText}"
VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
</Border>
<!-- 📊 数据表格 -->
<DataGrid Grid.Row="2"
ItemsSource="{Binding Employees}"
Style="{StaticResource ModernDataGrid}"
RowStyle="{StaticResource ModernDataGridRow}"
ColumnHeaderStyle="{StaticResource ModernDataGridColumnHeader}"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
SelectionMode="Single"
SelectionUnit="FullRow"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
AlternationCount="2">
<DataGrid.Columns>
<DataGridTextColumn Header="🆔 ID"
Binding="{Binding Id}"
Width="100"
IsReadOnly="True"/>
<DataGridTextColumn Header="👤 姓名"
Binding="{Binding Name}"
Width="*"
MinWidth="150"/>
<DataGridTextColumn Header="🏢 部门"
Binding="{Binding Department}"
Width="*"
MinWidth="150"/>
<DataGridTextColumn Header="💰 薪资"
Binding="{Binding Salary, StringFormat='{}{0:C}'}"
Width="130"/>
</DataGrid.Columns>
</DataGrid>
<!-- 📄 分页控件 -->
<Border Grid.Row="3"
Background="#F9F9F9"
CornerRadius="6"
Padding="15"
Margin="0,15,0,0">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Content="⏮️ 首页"
Command="{Binding FirstPageCommand}"
Style="{StaticResource PaginationButton}"
Width="80"
Height="35"
Margin="5,0"/>
<Button Content="⏪ 上一页"
Command="{Binding PreviousPageCommand}"
Style="{StaticResource PaginationButton}"
Width="80"
Height="35"
Margin="5,0"/>
<Border Style="{x:Null}"
Background="White"
CornerRadius="4"
BorderBrush="#E0E0E0"
BorderThickness="1"
Margin="20,0">
<TextBlock Text="{Binding PageInfo}"
Style="{StaticResource PageInfoText}"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
<Button Content="下一页 ⏩"
Command="{Binding NextPageCommand}"
Style="{StaticResource PaginationButton}"
Width="80"
Height="35"
Margin="5,0"/>
<Button Content="尾页 ⏭️"
Command="{Binding LastPageCommand}"
Style="{StaticResource PaginationButton}"
Width="80"
Height="35"
Margin="5,0"/>
</StackPanel>
</Border>
</Grid>
</Border>
</Grid>
</Window>
C#public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new EmployeeViewModel();
}
}

XML<!-- 必须设置的虚拟化属性 -->
<DataGrid VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
EnableRowVirtualization="True"
EnableColumnVirtualization="True">
C#// ❌ 错误做法:频繁更新整个集合
Employees = new ObservableCollection<Employee>(newData);
// ✅ 正确做法:清空后逐个添加
Employees.Clear();
foreach (var item in newData)
{
Employees.Add(item);
}
C#private async Task LoadDataWithProgressAsync()
{
var progress = new Progress<int>(value =>
{
// 更新进度条
LoadingProgress = value;
});
await Task.Run(() =>
{
// 数据加载逻辑
for (int i = 0; i < totalItems; i++)
{
// 处理数据
((IProgress<int>)progress).Report((i * 100) / totalItems);
}
});
}
C#// 实现页面缓存
private readonly Dictionary<string, PageResult<Employee>> _cache = new();
public async Task<PageResult<Employee>> GetPagedDataAsync(PageRequest request)
{
var cacheKey = $"{request.PageIndex}_{request.PageSize}_{request.SearchText}";
if (_cache.TryGetValue(cacheKey, out var cachedResult))
{
return cachedResult;
}
var result = await LoadDataFromDatabaseAsync(request);
_cache[cacheKey] = result;
return result;
}
C#// 预加载下一页数据
private async Task PreloadNextPageAsync()
{
var nextPageRequest = new PageRequest
{
PageIndex = CurrentPage + 1,
PageSize = PageSize,
SearchText = SearchText
};
// 后台预加载
_ = Task.Run(async () => await _service.GetPagedEmployeesAsync(nextPageRequest));
}
通过本文的完整实战,我们成功解决了WPF DataGrid大数据展示的三个核心问题:
三个"收藏级"关键点:
从WinForm到WPF的转型之路并不平坦,但掌握了这些核心技术,你就能轻松应对各种大数据场景。记住:好的用户体验,从优雅的数据展示开始!
💬 互动时间:
你在WPF DataGrid开发中遇到过哪些性能问题?欢迎在评论区分享你的经验,或者说说你希望了解的其他WPF进阶技巧!
觉得这篇文章对你有帮助吗?请转发给更多需要的同行! 🚀
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!