-1

I'm tasked with implementing a custom DataGrid-like control in one of my projects.

This is due to many specific features it needs to have implemented, and also the large amounts of data that have to be displayed. We're talking about a million rows on average, sometimes more.

So naturally I looked into virtualization and how to implement the built-in features WPF has, but I seem to have no chance getting it to work. I've also spent countless hours trying to find more resources on this, and all I can find is people using either the native ListBox or DataGrid - no custom implementations.

The problem is that my custom grid attempts to load all UI elements (rows) into view right away - crushing the memory and CPU load.

This is my source code. Perhaps someone proficient enough in WPF can help me pinpoint any possible cause of the VirtualizingStackPanel seemingly not working.

I'm unsure whether the code-behind here is needed, the control itself renders just fine. Of course, I'm also open to any alternatives, such as using DataGrid and replacing its templates instead, or writing the virtualization logic myself, if that seems more feasible due to any limitations.

<!-- Cell inherits ContentControl -->
<Style TargetType="{x:Type local:Cell}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:Cell}">
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <ContentPresenter HorizontalAlignment="Stretch" 
                            VerticalAlignment="Center" 
                            Margin="{TemplateBinding Padding}" />
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<!-- Row inherits ContentControl -->
<Style TargetType="{x:Type local:Row}">
  <Setter Property="Background" Value="Transparent"/>
  <Setter Property="Margin" Value="0,0,0,1"/>
  <Setter Property="Height" Value="24"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:Row}">
        <Border>
          <ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Grid}}, Path=Columns}">

            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <UniformGrid Rows="1" />
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.ItemTemplate>
              <DataTemplate DataType="{x:Type local:ColumnDefinition}">
                <local:Cell Background="White" 
                            Padding="4,2"
                            Height="24"
                            Margin="1">
                  <TextBlock>
                    <TextBlock.Text>
                      <MultiBinding Converter="{StaticResource CellConverter}">
                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type local:Row}}" 
                                   Path="DataContext" />
                        <Binding Path="." />
                      </MultiBinding>
                    </TextBlock.Text>
                  </TextBlock>
                </local:Cell>
              </DataTemplate>
            </ItemsControl.ItemTemplate>
          </ItemsControl>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<!-- Grid inherits ItemsControl -->
<Style TargetType="{x:Type local:Grid}">
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate>
        <VirtualizingStackPanel VirtualizationMode="Recycling"
                                IsVirtualizing="True" 
                                CacheLength="1,2"
                                CacheLengthUnit="Item" 
                                CanVerticallyScroll="True" 
                                ScrollUnit="Item"
                                Orientation="Vertical" 
                                IsItemsHost="True" />
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:Grid}">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
          </Grid.RowDefinitions>
          <ItemsControl Grid.Row="0" 
                        ItemsSource="{TemplateBinding Columns}">
            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <UniformGrid Rows="1" Height="40" />
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.ItemTemplate>
              <DataTemplate>
                <TextBlock Text="{Binding Name}" 
                           Background="#212224" 
                           Padding="8,4" 
                           FontWeight="Bold"
                           Foreground="#fafcfe"
                           MinWidth="128" />
              </DataTemplate>
            </ItemsControl.ItemTemplate>
          </ItemsControl>
          <ItemsPresenter Grid.Row="1"/>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
1
  • 1
    The "loading" of a "million records" isn't "virtualized"; the "rendering" is; unless it can't; because of how the XAML was implemented; which isn't evident just showing "templates"; which have too much lipstick; IMO. And yes, one can do their own virtualization by "paging"; or creating a "custom expanding" tree view; for example. Commented Nov 6 at 16:16

1 Answer 1

1

Somehow, I found the issue by aimlessly trying to set all kinds of (attached) Properties of my controls.

The key was, or is, to wrap the ItemsPresenter inside the custom control in a ScrollViewer and set its CanContentScroll explicitly to True.

To me, this is pretty wild, and it also screws up the item sizing within the VirtualizingStackPanel, but at least it does correctly virtualize its contents now.

<ScrollViewer Grid.Row="1" 
              CanContentScroll="True" 
              HorizontalScrollBarVisibility="Hidden" 
              VerticalScrollBarVisibility="Auto">
  <ItemsPresenter />
</ScrollViewer>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.