DataGrid with items from PowerShell very slow when source size large

214 Views Asked by At

Here's a simple WPF program (most of the code is in the code-behind):

MainWindow.xaml:

<Window x:Class="WpfTextBlockInlineDataGridPSProcessSource.MainWindow"
        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:local="clr-namespace:WpfTextBlockInlineDataGridPSProcessSource"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Diagnostics;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;

using static System.Console;

namespace WpfTextBlockInlineDataGridPSProcessSource
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();


            var runspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault());

            runspace.Open();

            var ps = PowerShell.Create();

            ps.Runspace = runspace;

            // var result = ps.AddScript("get-process").Invoke();

            var result = ps.AddScript("get-process | select-object -first 20").Invoke();


            WriteLine(result.Count);


            if (result.All(elt => elt.BaseObject is Process))
            {

                var data_grid = new DataGrid()
                {
                    ItemsSource = result,

                    IsReadOnly = true,

                    AutoGenerateColumns = false
                };

                data_grid.Columns.Add(new DataGridTextColumn()
                {
                    Header = "ProcessName",
                    Binding = new Binding("ProcessName"),
                });

                var text_block = new TextBlock();

                var scroll_viewer = new ScrollViewer();

                scroll_viewer.Content = text_block;

                text_block.Inlines.Add(data_grid);

                text_block.Inlines.Add(new LineBreak());

                var dock_panel = new DockPanel();

                dock_panel.Children.Add(scroll_viewer);

                Content = dock_panel;
            }
        }
    }
}

You'll need the System.Management.Automation.dll nuget package to build this test program.

Here's what it looks like when run:

enter image description here

Note that the DataGrid ItemsSource is set to the result of a PowerShell command.

In that case, we're only displaying 20 Process objects in the DataGrid.

With only 20 items, the program opens quickly and is responsive.

If we change this line:

var result = ps.AddScript("get-process | select-object -first 20").Invoke();

to the following:

var result = ps.AddScript("get-process").Invoke();

the program becomes non-responsive. Note that on my system, there are over 370 processes in the list; results may vary on your system.

How can the program be made to be responsive in the case of a large amount of items?

Please note that the particular arrangement of the WPF items:

DockPanel -> ScrollViewer -> TextBlock -> Inline -> DataGrid

is meant to simulate another program that this one is meant to demonstrate the issue in. So that's why it's setup in this way.

If there aren't enough processes running on your system to experience the slow down, you can also use something like the following:

var result = ps.AddScript("get-process | select-object -first 20; get-process | select-object -first 20; get-process | select-object -first 20").Invoke();

To simulate a larger number of processes. (Consider getting the count to over 300.)

1

There are 1 best solutions below

1
dharmatech On

Setting the MaxHeight of the DataGrid solved the issue:

var data_grid = new DataGrid()
{
    ItemsSource = result,

    IsReadOnly = true,

    MaxHeight = 800,

    AutoGenerateColumns = false
};

It's blazing fast now!

One theory is that, without a MaxHeight, the whole DataGrid is shown, thus turning off virtualization. MaxHeight causes a ScrollViewer to engage after a certain height is surpassed, which seems to allow for virtualization.