Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance issues on moving/hiding columns in large wpf datagrid

Tags:

c#

wpfdatagrid

I am using wpf datagrid to display large amounts of data (around 100 columns and 1000 rows). The columns are bound to properties dynamically added using typedescripter. By default the datagrid shows all the column however we have added functionality that allows user to only see subset of all columns and they can also change the order of displayed columns. I am currently achieving this by toggling the visibility property of the columns and changing their displayindex. However the performance of doing so really sucks.

Below is an example to reproduce the issue

The XAML looks pretty straight forward

<Window x:Class="WpfDataGridTestApplication.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    WindowState="Maximized">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Height="Auto" Margin="5">
        <CheckBox x:Name="ApplyColumns" Width="200" Margin="5" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked">Show Predefined Columns</CheckBox>
    </StackPanel>
    <DataGrid 
        x:Name="Grid" EnableColumnVirtualization="False"
         EnableRowVirtualization="False"
        Grid.Row="1" SelectionUnit="Cell"
        ItemsSource="{Binding MyDataView}">
    </DataGrid>
</Grid>

The code behind is as follows

    public partial class MainWindow : Window
{
    /// <summary>
    /// this dictionary stores the column name of columns to display and their displayIndex
    /// </summary>
    Dictionary<string,int> _predefinedColumns=new Dictionary<string, int>()
                                                  {
                                                      {"Column_8",0},
                                                      {"Column_9",1},
                                                      {"Column_11",2},
                                                      {"Column_14",3},
                                                      {"Column_12",4},
                                                      {"Column_34",5},
                                                      {"Column_78",6},
                                                      {"Column_54",7},
                                                      {"Column_88",8},
                                                      {"Column_98",9},
                                                      {"Column_90",10},
                                                      {"Column_51",11},
                                                      {"Column_100",12},
                                                      {"Column_35",13},
                                                      {"Column_112",14},
                                                      {"Column_101",15}
                                                  };
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MyClassViewModel();
    }

    /// <summary>
    /// Toggle display of only subset of columns
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void CheckBox_Checked(object sender, RoutedEventArgs e)
    {
        if (ApplyColumns.IsChecked ?? false)
        {
            foreach (var col in this.Grid.Columns)
            {
                if (_predefinedColumns.ContainsKey(col.Header as string))
                {
                    col.Visibility = Visibility.Visible;
                    col.DisplayIndex = _predefinedColumns[col.Header as string];
                }
                else
                {
                    col.Visibility = Visibility.Collapsed;
                }
            }
        }
        else
        {
            foreach (var col in this.Grid.Columns)
            {
                col.Visibility = Visibility.Visible;
                var header = col.Header.ToString();
                col.DisplayIndex = Int32.Parse(header.Substring(7)) - 1;
            }
        }
    }
}

This viewmodel reproduces large data using manually created DataTable. However in the actual code behind datagrid binds to class with dynamic property added using typedescripter

    public class MyClassViewModel
{
    public DataView MyDataView
    {
        get
        {
            var dt = new DataTable();
            foreach (var colNum in Enumerable.Range(1, 120))
            {
                dt.Columns.Add(String.Format("Column_{0}", colNum), Type.GetType("System.Int32"));
            }

            var r = new Random();
            for (int x = 1; x <= 1000; x++)
            {
                var dr = dt.NewRow();
                foreach (var colNum in Enumerable.Range(1, 120))
                {
                    dr[String.Format("Column_{0}", colNum)] = r.Next(100);
                }
                dt.Rows.Add(dr);
            }
            return dt.DefaultView;
        }
    }
}

I have tried the following but no luck so far

1. The performance is better if I turn on row and column virtualization. However this ruins the scrolling performance which is not acceptable (especially if you try drag the thumb).
2. Instead of changing display and visibility I tried removing all columns and then adding only the required ones but it did not effect performance either.

I will really appreciate any help with this. We don't want performance gains at the expense of adverse scrolling performance. So if virtualization needs to be turned on how can we improve scrolling performance. Or there any better way of achieving both good scrolling performance and good performance of column show/hide/move with such large datagrids.

like image 556
shomat Avatar asked Dec 01 '11 05:12

shomat


1 Answers

You really should leave Virtualization on when using larger data sets, otherwise every cell is going through the layout / measure pass even if it's not rendered on screen. I've had some success by using ScrollViewer.IsDeferredScrollingEnabled="False" to tweak the scrolling behavior, but overall, haven't had much luck with performance with big datasets in the WPF datagrid.

like image 179
GlitterMonkey Avatar answered Oct 20 '22 21:10

GlitterMonkey