WrapPanel 实现虚拟化
控件名:VirtualizingWrapPanel
作者:WPFDevelopersOrg
原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用大于等于
.NET40
;Visual Studio 2022
;项目使用 MIT 开源许可协议;
众所周知 WPF 的 StackPanel 在加载大量数据时性能会特别差,但是官方提供了一个虚拟化容器VirtualizingStackPanel[1] ;
VirtualizingStackPanel.IsVirtualizing
附加属性设置为true
时就开启虚拟化。VirtualizingStackPanel.IsVirtualizing
附加属性设置为false
其VirtualizingStackPanel
行为与普通StackPanel
属性的行为相同。
WrapPanel 默认是不支持虚拟化的,所以需要自行实现。
1) VirtualizingWrapPanel 查看源码1[2] | VirtualizingWrapPanel 查看源码2[3]。
2) 准备数据HospitalList.cs如下:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Media;namespace WPFDevelopers.Minimal.Sample.Models
{public class HospitalList : ObservableCollection<Hospital>{public HospitalList(){var hospitals = new string[] { "No. 189, Grove St, Los Angeles", "No. 3669, Grove St, Los Angeles" };var names = new string[] { "Doctor Fang", "Judge Qu" };var images = new string[] { "https://pic2.zhimg.com/80/v2-0711e97955adc9be9fbcff67e1007535_720w.jpg",//"https://pic2.zhimg.com/80/v2-5b7f84c63075ba9771f6e6dc29a54615_720w.jpg","https://pic3.zhimg.com/80/v2-a3d6d8832090520e7ed6c748a8698e4e_720w.jpg","https://pic3.zhimg.com/80/v2-de7554ac9667a59255fe002bb8753ab6_720w.jpg"};var state = 0;for (var i = 1; i < 10000; i++){Add(new Hospital { Id = $"9999{i}", DoctorName = i % 2 == 0 ? names[0]:names[1], HospitalName = i % 2 == 0 ? hospitals[0] : hospitals[1] ,State = state ,UserImage = images[state] });state++;if (state > 2)state = 0;}}}public class Hospital{public string Id { get; set; }public string DoctorName { get; set; }public string HospitalName { get; set; }public string UserImage { get; set; }public int State { get; set; }}
}
3) 新建展示VirtualizingWrapPanelExample.xaml如下:
<ws:Window x:Class="WPFDevelopers.Minimal.Sample.ExampleViews.VirtualizingWrapPanelExample"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:ws="https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal"xmlns:local="clr-namespace:WPFDevelopers.Minimal.Sample.ExampleViews"xmlns:model="clr-namespace:WPFDevelopers.Minimal.Sample.Models"xmlns:converts="clr-namespace:WPFDevelopers.Minimal.Sample.Converts"mc:Ignorable="d" WindowStartupLocation="CenterScreen"Title="System V1.0" Height="450" Width="900"><Window.Resources><model:HospitalList x:Key="myHospitalList"/><converts:StateConvert x:Key="stateConvert"></converts:StateConvert></Window.Resources><Grid Margin="4"><WrapPanel HorizontalAlignment="Left"><WrapPanel.Resources><Style TargetType="Border"><Setter Property="Padding" Value="2"></Setter><Setter Property="BorderThickness" Value="1"></Setter></Style><Style TargetType="Rectangle"><Setter Property="Width" Value="15"></Setter><Setter Property="Height" Value="15"></Setter><Setter Property="Opacity" Value=".2"></Setter></Style></WrapPanel.Resources><WrapPanel><Border BorderBrush="Green"><Rectangle Fill="Green"/></Border><TextBlock Text="Idle" Foreground="Black" Margin="4,0"/></WrapPanel><WrapPanel><Border BorderBrush="Orange"><Rectangle Fill="Orange"/></Border><TextBlock Text="Slightly Idle" Foreground="Black" Margin="4,0"/></WrapPanel><WrapPanel><Border BorderBrush="Red"><Rectangle Fill="Red"/></Border><TextBlock Text="Busy" Foreground="Black" Margin="4,0"/></WrapPanel></WrapPanel><TextBlock HorizontalAlignment="Right" Foreground="Black"Margin="4,2" FontSize="16"><Run Text="Count:"></Run><Run Text="{Binding ElementName=DocumentsList,Path=.Items.Count,Mode=OneTime}"></Run></TextBlock><ListBox x:Name="DocumentsList"ItemsSource="{Binding Source={StaticResource myHospitalList}}"Margin="0,24,0,0"><ListBox.ItemTemplate><DataTemplate><Border BorderBrush="{Binding State,Converter={StaticResource stateConvert}}" BorderThickness="1"Width="196"Height="94"><Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Rectangle Fill="{Binding State,Converter={StaticResource stateConvert}}" Opacity=".2" Grid.ColumnSpan="2" Grid.RowSpan="3"/><Border Grid.RowSpan="2" Grid.Column="0" Width="60" Height="60"Margin="0,4,0,0" CornerRadius="10"><Border.Background><ImageBrush ImageSource="{Binding UserImage}" Stretch="Uniform"/></Border.Background></Border><TextBlock Grid.Column="1" Grid.Row="0"Text="{Binding Path=Id}" Margin="0,4,0,0"/><TextBlock Grid.Column="1" Grid.Row="1"Text="{Binding Path=DoctorName}"/><TextBlock Grid.ColumnSpan="2" Grid.Row="2"Padding="10,0"Text="{Binding Path=HospitalName}" TextTrimming="CharacterEllipsis"/></Grid></Border></DataTemplate></ListBox.ItemTemplate><ListBox.Template><ControlTemplate><Border CornerRadius="2" BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><ScrollViewer x:Name="ScrollViewer"Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0" IsTabStop="False"><ItemsPresenter /></ScrollViewer></Border></ControlTemplate></ListBox.Template><ListBox.ItemsPanel><ItemsPanelTemplate><ws:VirtualizingWrapPanel ItemWidth="200"ItemHeight="100"/></ItemsPanelTemplate></ListBox.ItemsPanel></ListBox></Grid>
</ws:Window>
4) 状态StateConvert.cs如下:
using System;
using System.Windows.Data;
using System.Windows.Media;namespace WPFDevelopers.Minimal.Sample.Converts
{public class StateConvert : IValueConverter{public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo){var color = Brushes.Green;if (value != null){var state = int.Parse(value.ToString());switch (state){case 0:color = Brushes.Green;break;case 1:color = Brushes.Orange;break;case 2:color = Brushes.Red;break;}}return color;}public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo){throw new NotImplementedException();}}
}
参考资料
[1]
VirtualizingStackPanel: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.controls.virtualizingstackpanel?view=windowsdesktop-6.0
[2]VirtualizingWrapPanel 查看源码1: https://github.com/samueldjack/VirtualCollection/blob/master/VirtualCollection/VirtualCollection/VirtualizingWrapPanel.cs
[3]VirtualizingWrapPanel 查看源码2: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Controls/VirtualizingWrapPanel/VirtualizingWrapPanel.cs