?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