Closed h3xds1nz closed 1 month ago
Displaying large amounts of data in WPF:
Method 1: Simple style (important) + virtualization
Method 2: Paged loading
According to my test, your virtualization does not seem to be enabled successfully. Please make sure your virtualization is correct
The following example uses ListView to load 2,000,000 pieces of data without lag when sliding.
<Grid>
<Grid.DataContext>
<vm:MainViewModel x:Name="MyData"></vm:MainViewModel>
</Grid.DataContext>
<Grid.Resources>
<DataTemplate x:Key="MyListBoxItem">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Id}" Margin="20,0,20,0"></TextBlock>
<TextBlock Text="{Binding Path=Name}" Margin="20,0,20,0"></TextBlock>
<TextBlock Text="{Binding Path=Email}" Margin="20,0,20,0"></TextBlock>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<StackPanel Height="400">
<!--<ListBox VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" ItemsSource="{Binding Path=Users}" ItemTemplate="{StaticResource MyListBoxItem}" Height="400">
</ListBox>-->
<ListView VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" ItemsSource="{Binding Path=Users}" ItemTemplate="{StaticResource MyListBoxItem}" Height="400"></ListView>
</StackPanel>
</Grid>
ViewModel
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<User> users;
public ObservableCollection<User> Users { get { return users; } set { users = value;OnPropertyChanged("Users"); } }
public MainViewModel() {
Users = GetUsers(2000000);
MessageBox.Show("data create success");
}
public ObservableCollection<User> GetUsers(int Count)
{
ObservableCollection<User> Users = new ObservableCollection<User>();
for(int i = 0; i < Count; i++)
{
Users.Add(new User() { Id = i,Name="AAAA_"+i,Email="BBBB_"+i }); ; ;
}
return Users;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Model
public class User:INotifyPropertyChanged
{
private int id;
public int Id { get { return id; } set { id = value;OnPropertyChanged("Id"); } }
private string name;
public string Name { get { return name; } set { name = value; OnPropertyChanged("Name"); } }
private string email;
public string Email { get { return email; } set { email = value; OnPropertyChanged("Email"); } }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
@huiliuss Thank you for your comment, should you check the reproduction project and the steps included to reproduce the issue reported, you will notice your example is lacking necessary steps to successfully reproduce it. That's why I've included one in the first place.
1) You do not need to enable virtualization explicitly by setting the attached property of VirtualizingPanel
for ListView (GridView) and ListBox in WPF as it uses VirtualizingStackPanel
and it is set to true by default, so unless you change the template to include a different ItemsControl/ItemsPanel for example, you're fine (none of those things are done by my repro project).
2) The problem doesn't exist until automation peers are being created, which can be easily triggered in common scenarios by using a ComboBox
and displaying the dropdown. Again, something your example project does not do.
3) If I really didn't use virtualization, I'm looking at at least 16 GB of item containers in memory and wonderful amount of time to create those spend both in GC and actual creation time (over 15 minutes at least). Believe me, I didn't do that.
The equivalent item you mentioned is not specifically used in ComboBox. Can you provide the corresponding usage code so that I can further confirm the effect you want to achieve?
It appears I was able to solve my data grid sluggish issue by making my own DataGrid class and returning null in OnCreateAutomationPeer. I'm not sure what ramifications this may have but it appears to work,.
public class MyDataGrid : DataGrid
{
public MyDataGrid()
{
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return null;
//return base.OnCreateAutomationPeer();
}
}
@brswan Basically entire UI Automation won't work for that control. Which means any accessibility tools.
@brswan Basically entire UI Automation won't work for that control. Which means any accessibility tools.
After more testing it doesn't seem to help datagrids with any sort of complexity to them. It's still an issue for me.
I found a solution that works for my problem. I had to override the Windows class. From there you override the OnCreateAutomationPeer() method.
public class CustomWindowAutomationPeer : FrameworkElementAutomationPeer
{
public CustomWindowAutomationPeer(FrameworkElement owner) : base(owner) { }
protected override string GetNameCore()
{
return "CustomWindowAutomationPeer";
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}
protected override List<AutomationPeer> GetChildrenCore()
{
return new List<AutomationPeer>();
}
}
In your custom Window class add
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
return new CustomWindowAutomationPeer(this);
}
Description
I was running some tests with ListView (using GridView) and when I loaded
1 000 000
items (yes, an obscure use case but that's why we got virtualization, no?), the scrolling performance would degrade and keyboard scrolling felt almost unusable.During a quick rundown, I've discovered the culprit is in override of
GetChildrenCore
ofGridViewItemAutomationPeer
, which performs IndexOf (linear) search in theItems
collection of the underlyingItemsSource
to find the respective item's Row.Reproduction Steps
The easiest step to reproduce is to add a ListView with GridView plus a ComboBox, after opening the ComboBox's dropdown (clicking it),
Accessibility.dll
gets loaded and the party gets started. You can find a demo repository on Github - here.Behavior
The scrolling performance becomes worse to unusable with increasing number of items.
Known Workarounds
The "workaround" is to subclass
GridView
, overridingGetAutomationPeer
to return NULL. Not really a solution though.Other information
I have created a pull request at the same time, fixing the respective issue. #9182