数据网格速度较慢 - 提高性能

本文关键字:高性能 数据网 网格 速度 数据 | 更新日期: 2023-09-27 18:37:08

我有一个相当大的数据网格。

下面是只有一列的数据网格:

<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>

让我解释一下:

"highlightConverter"检查单元格的值,并进行某种语法突出显示,语法检查并返回彩色TextBlock

如果我想编辑该TextBlock我必须将其转换为TextBox

网格有两个组(我没有发布所有列,所以组不在这里,而是GroupStyle

两个有排序箭头,我也在这里添加了它们。在每一列标题上都有一个过滤器按钮来过滤行。

这让我想到了我的问题:网格在过滤、排序、调整行/列大小、重新排序列、滚动等方面变得非常慢。我认为这是因为TextBlockTextBox项目。有没有更好的解决方案来提高性能?

这是亮点转换器.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;
            }
        }
    }
}

数据网格速度较慢 - 提高性能

在我看来

,这不是使用IMultiConverter的问题,而是VirtualizationColumn Rendering的问题。

尝试对数据网格使用这些属性:

  • 为网格启用 VirtualizingStackPanel.VirtualizationMode
  • Set VirtualizingStackPanel.IsVirtualizing="true" for DataGrid
  • 最大宽度="2560" 最大高度="1600"

切勿将 DataGrid 放在 ScrollViewer 中,因为您基本上会失去虚拟化。

例如:

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

你有相当大的Convert方法。假设它为 DataGrid 中的每个单元格调用。

  1. 尝试将语法分析带到另一个线程。对于用户来说,它看起来像 - 输入的文本并在几秒钟内检查和/或突出显示语法。

  2. Regex.Matches使用延迟初始化,因此当您遍历匹配项时 - 它实际上每次都基于正则表达式在字符串中执行搜索。尝试排除assignColor Convert()方法组并检查执行时间。如果有帮助 - 尝试编写自己的字符串解析器,不使用正则表达式。

  3. 在这一行中List<Char> splittedInput = input.ToCharArray().ToList();您将遍历input字符两次。第一次 - 从字符串字符创建数组时,第二次 - 从数组项创建列表时。然后你在 foreach (Char character in splittedInput) 中再次迭代这个字符串。您实际上可以遍历input本身:foreach (var character in input)

  4. 在这一行中:if (values.Count() == 2)使用.Length(数组的属性)而不是.Count()(扩展 LINQ 方法),因为在每个时刻数组都知道它的长度,并且调用扩展 linq 方法会导致处理数组,就像处理IEnumerable一样延迟初始化。

但在我看来,主要问题在于连续的文本分析。常见的解决方案是在并行线程中执行字符串分析 - 用于非暂停用户输入(或对此执行任何操作)。