简体   繁体   中英

Slow datagrid - improve performance

I have a pretty huge Datagrid.

Here's the datagrid with just one column:

<DataGrid x:Name="dgVarConfig"
              ItemsSource="{Binding VarConfigList, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
              SelectedItem="{Binding Path=SelectedVarConfig, Mode=TwoWay}"
              Margin="8,56,557,80"
              AutoGenerateColumns="False"
              CanUserDeleteRows="False"
              CanUserResizeRows="False"
              HeadersVisibility="Column"
              CanUserAddRows="False"
              HorizontalScrollBarVisibility="Auto"
              PreviewKeyDown="dgVarConfig_PreviewKeyDown"
              BeginningEdit="dgVarConfig_BeginningEdit"
              CellEditEnding="dgVarConfig_CellEditEnding"
              SelectionChanged="dgVarConfig_SelectionChanged"
              EnableRowVirtualization="True"
              EnableColumnVirtualization="False"
              VerticalGridLinesBrush="Black"
              VirtualizingStackPanel.VirtualizationMode ="Standard"
              VirtualizingStackPanel.IsVirtualizing="true">


<DataGrid.Columns>
    <DataGridTemplateColumn Width="auto"  MinWidth="150" SortMemberPath="Match_expression">

        <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ContentControl>
                            <MultiBinding Converter="{StaticResource highlightConverter}" ConverterParameter="MATCHEXPRESSION">
                                <Binding Path="Match_expression"></Binding>
                            </MultiBinding>
                        </ContentControl>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>

                <DataGridTemplateColumn.CellEditingTemplate >
                    <DataTemplate>
                        <TextBox Text="{Binding Match_expression}" FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" Style="{StaticResource GridTextBox}"></TextBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>

                <DataGridTemplateColumn.HeaderStyle>
                    <Style TargetType="DataGridColumnHeader">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="DataGridColumnHeader">
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="auto"></ColumnDefinition>
                                            <ColumnDefinition Width="*"></ColumnDefinition>
                                            <ColumnDefinition Width="auto"></ColumnDefinition>
                                            <ColumnDefinition Width="auto"></ColumnDefinition>
                                            <ColumnDefinition Width="auto"></ColumnDefinition>
                                        </Grid.ColumnDefinitions>

                                        <Label Grid.Column="1" Padding="5 0" Content="Auswahlformel" VerticalAlignment="Center" Cursor="Hand" Foreground="White"/>
                                        <TextBox x:Name="txtMatchExpressionFilter" Margin="0 5" Grid.Column="2" Width="150" Visibility="Collapsed"></TextBox>
                                        <Button Grid.Column="3" Margin="5 0" x:Name="btnFilterMatchExpresion" Style="{StaticResource MyButton}" Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Right"  Click="btnFilterMatchExpresion_Click">
                                            <Button.Background>
                                                <ImageBrush ImageSource="Resources/filter.png"/>
                                            </Button.Background>
                                        </Button>

                                        <Path x:Name="SortArrow"
                                        Grid.Column="0"
                                        HorizontalAlignment="Right" VerticalAlignment="Center"                                           
                                        Width="8" Height="6" Margin="2,0,5,0"
                                        Stretch="Fill" Opacity="0.5" Fill="White"
                                        RenderTransformOrigin="0.5,0.4"
                                        Visibility="Collapsed"
                                        Data="M0,0 L1,0 0.5,1 z" />

                                        <Thumb x:Name="PART_RightHeaderGripper" Grid.Column="4" HorizontalAlignment="Right" Width="1" BorderThickness="1" 
                                               BorderBrush="{Binding VerticalGridLinesBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" Cursor="SizeWE"/>
                                    </Grid>


                                    <ControlTemplate.Triggers>
                                        <Trigger Property="SortDirection" Value="Ascending">
                                            <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                                            <Setter TargetName="SortArrow" Property="RenderTransform">
                                                <Setter.Value>
                                                    <RotateTransform Angle="180" />
                                                </Setter.Value>
                                            </Setter>
                                        </Trigger>
                                        <Trigger Property="SortDirection" Value="Descending">
                                            <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGridTemplateColumn.HeaderStyle>

    </DataGridTemplateColumn>
</DataGrid.Columns>

<DataGrid.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type GroupItem}">
                                    <StackPanel Background="Gray" Margin="-5 0 0 0">
                                        <TextBlock Foreground="White" FontWeight="Bold" Text="{Binding Path=Name, StringFormat=Gruppe: {0}}" Margin="10 5 0 5"/>
                                        <ItemsPresenter />
                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>

            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <DockPanel Background="LightGray">
                            <TextBlock Text="{Binding Path=Name}" Foreground="Black" Margin="10 2 0 2"/>
                            <ItemsPresenter></ItemsPresenter>
                        </DockPanel>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
</DataGrid.GroupStyle>

<DataGrid.ContextMenu>
    <ContextMenu>
    <MenuItem x:Name="cmAddGroup" Header="Gruppe zuweisen" Click="cmAddGroup_Click" ></MenuItem>
    <MenuItem x:Name="cmRemoveDeleteFlag" Header="Löschvermerk entfernen" Click="cmRemoveDeleteFlag_Click"></MenuItem>
    </ContextMenu>          
</DataGrid.ContextMenu>

Let me explain:

"highlightConverter" checks the value of the cell and does some sort of syntaxhighlighting, syntaxcheck and returns a colored TextBlock

If i want to edit that TextBlock i had to convert it to a TextBox

The grid has two groups (i havent posted all columns, so the groups aren't here, but the GroupStyle )

Two have the SortingArrows i added them here too. On each column Header there is a filterButton to filter the rows.

That leads me to my problem: The grid gets really really slow on filtering, sorting, resize rows/columns, reorder columns, scrolling etc. I think its because of the TextBlock and TextBox items. Is there any better solution to improve the performance?

Here's the HighlightConverter.cs

public class HighlightConverter : IMultiValueConverter
{
    public static Dictionary<String, SyntaxResult> calcFormulaCache;
    public static Dictionary<String, SyntaxResult> matchExpressionCache;

    public HighlightConverter()
    {
        calcFormulaCache = new Dictionary<string, SyntaxResult>();
        matchExpressionCache = new Dictionary<string, SyntaxResult>();
    }

    ClientSettings clientSettings = new ClientSettings();

    Brush[] colorArray;

    Regex subFormula = new Regex(@"\w+\(\)");
    Regex sapFormula = new Regex(@"\w+\(([^)]+)\)");
    Regex strings = new Regex(@"\'[^']+\'");
    Regex numerals = new Regex(@"\b[0-9\.]+\b");
    Regex characteristic = new Regex(@"(?:)?\w+(?:)?");
    Regex andOr = new Regex(@"( and )|( AND )|( or )|( OR )");
    Regex not = new Regex(@"(not )|(NOT )");

    VariantConfigurationTestDAO variantConfigurationTestDAO = new VariantConfigurationTestDAO();


    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (!clientSettings.SyntaxCheck && !clientSettings.SyntaxCheck)
            return values[0];

        TextBlock textBlock = new TextBlock();

        string input = values[0] as String;

        if (!String.IsNullOrEmpty(input))
        {
            if (clientSettings.SyntaxHighlighting)
            {
                colorArray = new Brush[input.Length];

                for (int i = 0; i < input.Length; i++)
                    colorArray[i] = Brushes.Black;

                //Reihenfolge beibehalten!!
                assignColor(Brushes.Blue, characteristic.Matches(input));
                assignColor(Brushes.Black, andOr.Matches(input));
                assignColor(Brushes.Black, numerals.Matches(input));
                assignColor(Brushes.Orange, strings.Matches(input));
                assignColor(Brushes.DeepPink, subFormula.Matches(input));
                assignColor(Brushes.Green, sapFormula.Matches(input));
                assignColor(Brushes.Green, not.Matches(input));

                List<Char> splittedInput = input.ToCharArray().ToList();
                int index = 0;
                foreach (Char character in splittedInput)
                {
                    textBlock.Inlines.Add(new Run(character.ToString()) { Foreground = colorArray[index] });
                    index++;
                }

                colorArray = null;
            }
            else
            {
                colorArray = null;
                textBlock.Text = input;
            }

            if (clientSettings.SyntaxCheck)
            {
                Pen pen = new Pen(Brushes.Red, 3);
                TextDecoration textDec = new TextDecoration(TextDecorationLocation.Underline, pen, 4, TextDecorationUnit.Pixel, TextDecorationUnit.FontRecommended);


                 if (!String.IsNullOrEmpty((String)parameter))
                 {
                     String para = (String)parameter;
                     SyntaxResult syntaxResult = null;

                     switch (para)
                     {
                         case "VARIANT":
                             if (para == "VARIANT")
                             {
                                 if (characteristic.IsMatch(input) || subFormula.IsMatch(input))
                                 {
                                     if (andOr.IsMatch(input) || numerals.IsMatch(input) || strings.IsMatch(input) || sapFormula.IsMatch(input) || not.IsMatch(input))
                                     {
                                         textBlock.TextDecorations.Add(textDec);
                                         textBlock.ToolTip = "Hier darf nur ein Merkmal oder eine Subformel stehen";
                                     }
                                 }
                             }
                             break;
                         case "CALCFORMULA":
                             if (!calcFormulaCache.ContainsKey(input))
                                 calcFormulaCache.Add(input, variantConfigurationTestDAO.vcCalculateFormula3(input, true));

                             syntaxResult = calcFormulaCache[input];
                             break;
                         case "MATCHEXPRESSION":
                             if (!matchExpressionCache.ContainsKey(input))
                                 matchExpressionCache.Add(input, variantConfigurationTestDAO.vcEvalMatchEx(input, true));


                             syntaxResult = matchExpressionCache[input];;
                             break;
                         default:
                             break;
                     }


                     if (syntaxResult != null)
                     {
                         if (syntaxResult.syntax > 0)
                         {
                             textBlock.TextDecorations.Add(textDec);

                             if (syntaxResult.errors.Count == 0)
                                 textBlock.ToolTip = "Allgemeiner Syntaxfehler";
                             else
                                 textBlock.ToolTip = String.Join(",", syntaxResult.errors);
                         }
                     }

                 }

                if (values.Count() == 2)
                {
                    string input2 = values[1] as String;

                    if (String.IsNullOrEmpty(input2))
                    {
                        textBlock.TextDecorations.Add(textDec);
                        textBlock.ToolTip = "Es müssen Variante und Kalkulationsformel gefüllt sein";
                    }
                }
            }
        }



        return textBlock;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }


    private void assignColor(Brush brush, MatchCollection matchCollection)
    {
        foreach (Match match in matchCollection)
        {
            int start = match.Index;
            int end = start + match.Length;

            for (int i = start; i < end; i++)
            {
                colorArray[i] = brush;
            }
        }
    }

}

In my view, it is not a problem of using IMultiConverter , but Virtualization and Column Rendering .

Try to use these properties to your DataGrid:

  • Enabled VirtualizingStackPanel.VirtualizationMode for a Grid
  • Set VirtualizingStackPanel.IsVirtualizing="true" for DataGrid
  • MaxWidth="2560" MaxHeight="1600"

Never put a DataGrid in a ScrollViewer, because you will essentially lose virtualization .

For example:

<DataGrid ItemsSource="{Binding EmployeeDataTable, IsAsync=True}" 
          VirtualizingStackPanel.IsVirtualizing="true" EnableRowVirtualization="True"
        EnableColumnVirtualization="True" MaxWidth="2560" MaxHeight="1600"  
        VirtualizingStackPanel.VirtualizationMode="Recycling"   
        VirtualizingPanel.IsVirtualizingWhenGrouping="True"/>

You have rather big Convert method. Imagine that it invokes for each cell in your DataGrid.

  1. Try to take syntax analysis to another thread. For user it will look like - text entered and in couple of seconds syntax will be checked and/or highlighted.

  2. Regex.Matches use lazy initialization, so when you are iterating through matches - it actually perform search in string based on regular expression, each time. Try to exclude assignColor methods group in Convert() and check execution time. If it helps - try to write your own string parser, without regular expressions.

  3. In this line List<Char> splittedInput = input.ToCharArray().ToList(); you are iterating twice through input characters. First time - when you are creating array from string characters, and second time - when you are creating list from array items. And then you are iterating again through this string in foreach (Char character in splittedInput) . You can actually iterate through input itself : foreach (var character in input) .

  4. In this line: if (values.Count() == 2) use .Length (property of array) instead of .Count() (extension LINQ method), because in each moment of time array knows it's length, and calling extension linq method cause to deal with array like with IEnumerable with lazy initialization.

But in my opinion main problem is in consecutive text analysis. Frequent solution is to perform string analysis in parallel thread - for non-pausing user input (or performing any actions on this).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM