Reputation: 10813
I cannot get this ItemsControl
to virtualize properly. Debugging shows that the collection is initialized quickly, but all the items are being added to the control instead of a subset (I simply put a TracePoint in the TextBox_Initializeevent in the
UserControl` that makes up the item).
Note: I have looked at other, similar questions, but have been unable to solve this issue with those answers.
The ViewModel:
public class ImportInformationViewModel : CommandViewModel
{
public ImportInformationViewModel()
{
this.PropertyChanged += ImportInformationViewModel_PropertyChanged;
}
private ObservableCollection<SingleTransactionViewModel> mTransactions;
public ReadOnlyObservableCollection<SingleTransactionViewModel> Transactions
{
get
{
if (mTransactions == null)
mTransactions = new ObservableCollection<SingleTransactionViewModel>();
var filtered = mTransactions.Where(trans => !trans.IgnoreTransaction)
.OrderBy(trans => trans.DateStamp)
.ThenBy(trans => trans.TransactionName)
.ThenBy(trans => trans.TransactionDetail);
return new ReadOnlyObservableCollection<SingleTransactionViewModel>(new ObservableCollection<SingleTransactionViewModel>(filtered));
}
}
private void ImportInformationViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "TransactionFileName")
{
if (File.Exists(mTransactionFileName))
{
mTransactions.Clear();
// Process File (Not Shown)
mTransactions.Add(new SingleTransactionViewModel()
{
DateStamp = date,
TransactionDetail = someText;
});
}
}
if (e.PropertyName != "Transactions")
NotifyPropertyChanged("Transactions");
}
}
SingleTransactionViewModel
is just another class that implements INotifyPropertyChanged
. Nothing special.
Here is the control containing the ItemsControl
<UserControl x:Class="ImportInformationView">
<UserControl.Resources>
<CollectionViewSource x:Key="TransactionsData" Source="{Binding Transactions}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="YearAndMonth" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<BooleanToVisibilityConverter x:Key="booleanToVisibility" />
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource TransactionsData}}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
MinHeight="20">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Padding="5" Margin="2,5">
<GroupBox.Header>
<Border Background="Black"
CornerRadius="4">
<TextBlock Text="{Binding Name}" />
</Border>
</GroupBox.Header>
<ItemsPresenter />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" Columns="2" Grid.IsSharedSizeScope="True" VirtualizingStackPanel.IsVirtualizing="True" />
<!--<VirtualizingStackPanel IsItemsHost="True" />-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:SingleTransactionView Margin="4,6" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
And I've boiled the SingleTransactionView
down to something very simple:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="ExpanderColumn" />
<ColumnDefinition Width="*" SharedSizeGroup="TransactionNameColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="DateColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="OptionsColumn" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="1"
Grid.Row="0"
Initialized="TextBlock_Initialized"
Text="{Binding TransactionName}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="14"
FontWeight="Bold"
Margin="4"/>
</Grid>
Upvotes: 0
Views: 1338
Reputation: 25623
This is probably due to your use of grouping. By default, enabling grouping effectively switches off virtualization. However, as of .NET 4.5, you should be able to regain this functionality via the VirtualizingPanel.IsVirtualizingWhenGrouping
property.
Excerpt from this blog post on WPF enhancements in .NET 4.5:
In WPF 4.0, you lost virtualization when grouping is done on the collection you display. I repeat : Grouping = no virtualization in WPF 4.0. This is still the default behavior of WPF 4.5, but you can turn on the virtualization by using the
IsVirtualizingWhenGrouping
attached property of theVirtualizingPanel
class. When this is done, you benefit of all the already described advantages of virtualization.Here is how you can enable it via XAML:
<ListBox ItemsSource="{Binding Persons}" ItemTemplate="{StaticResource PersonDataTemplate}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True"> <ListBox.GroupStyle> <GroupStyle HeaderTemplate="{StaticResource GroupHeaderTemplate}" /> </ListBox.GroupStyle> </ListBox>
Sounds like setting VirtualizingPanel.IsVirtualizingWhenGrouping="True"
alongside your other virtualization-related properties should give you the behavior you want.+
Upvotes: 2