【问题标题】:Lifeupdate DataGrid from TextFile with good performance具有良好性能的文本文件的 Life Update DataGrid
【发布时间】:2016-06-06 22:08:43
【问题描述】:

实际状态:

我有一个DataGrid,有 4 列(图标|日期时间|日志级别|消息)

我用它作为查看器来显示LogFile 的条目。 打开Window 时,UI 滞后,大量条目被一一添加到DataGrid

注意:我已经在使用多个线程。我的 UI 线程没有冻结。填满整个DataGrid 需要很长时间。

我想要什么:

我更喜欢在将整个窗口显示给用户之前“预渲染”整个窗口。

当我已经打开 Window 一次时 - 每次我再次打开它时,它都没有问题了。(不呈现新的......?)

我尝试过的:

  • Visibility 设置为Hidden 并等待(Thread.Sleep()) 10 秒然后设置Visibility = Visibility.Visible;
  • 在 ViewModel-Constructor 中将所有数据添加到我的 DataGrid

但这一切并没有真正解决它。我什至不确定是 C# 代码还是只是绑定...

这可能是一个愚蠢的问题,但有没有办法在显示之前“预渲染”DataGrid 及其Content

编辑:

我也使用一些DataTriggers 来设置 RowColor 但这可能不是问题..

这是我使用的一些代码:

入门类:

 public class LogEntry
{
    public string LogLevel { get; set; }
    public string LogLevelIcon
    {
        get
        {
            switch(LogLevel)
            {
                case "[D]":     //IF DEBUG ENTRY:
                    return "pack://application:,,,/Resources/Bug.png";
                case "[F]":     //IF FATAL ENTRY
                    return "pack://application:,,,/Resources/System-error-alt.png";
                case "[E]":     //IF ERROR ENTRY
                    return "pack://application:,,,/Resources/Error_32_WhiteBackground.png";
                case "[I]":     //IF INFO ENTRY
                    return "pack://application:,,,/Resources/Info_32.png";
                case "[W]":     //IF WARNING ENTRY
                    return "pack://application:,,,/Resources/Warning_32_WhiteBackground.png";
                case "[DB]":    //IF DB ENTRY
                    return "pack://application:,,,/Resources/Database.png";
                default:
                    return string.Empty;
            }                
        }
    }
    public string Message { get; set; }
    public DateTime DateTime { get; set; }

    public override string ToString()
    {
        return $"{LogLevel};{DateTime.ToString("dd.MM.yyyy HH:mm:ss")};{Message}";
    }
}

从我的日志文件中获取数据:

public void ExtractDataFromLogFile(string logFilePath)
    {
        new Thread(() => {

            List<string> linesInFile = new List<string>();
            using (FileStream stream = File.Open(logFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    while (true)
                    {
                        while (!reader.EndOfStream)
                        {
                            ProcessFileContent(reader.ReadLine());
                        }
                        while (reader.EndOfStream)
                        {
                            Thread.Sleep(50);
                        }
                    }
                }
            }

        }).Start();
    }

添加到ObservableCollection&lt;LogEntry&gt;() _logEntries;:

private void ProcessFileContent(string line)
    {
        Match match = _regex.Match(line);
        if (match.Success)
        {
            LogEntry entry = new LogEntry()
            {
                LogLevel = match.Groups[1].ToString(),
                DateTime = DateTime.Parse(match.Groups[2].ToString(), new CultureInfo("de-DE")),
                Message = match.Groups[3].ToString()
            };                    
            _logEntries.Add(entry);                    
        }
    }

最后是 DataGrid 的 XAML(样式省略了!):

<DataGrid Grid.Row="1"
          x:Name="DataGrid"
          Grid.ColumnSpan="2"
          Margin="5"
          IsReadOnly="True"
          AutoGenerateColumns="False"
          CanUserReorderColumns="False"
          ItemsSource="{Binding Path=ItemsView, UpdateSourceTrigger=PropertyChanged}">
  <DataGrid.Columns>
            <DataGridTemplateColumn Width="Auto">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Image Source="{Binding Path=LogLevelIcon, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
                               Width="16" 
                               Height="16"></Image>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTextColumn Width="Auto" Header="Datum"
                                Binding="{Binding Path=DateTime, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>               
            <DataGridTextColumn Width="*" Header="Meldung"
                                Binding="{Binding Path=Message, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid.Columns>
    </DataGrid>

注意“ItemsView”的类型是ICollectionView

我在这里填写:

private void InitializeCollection()
    {
        ItemsView = CollectionViewSource.GetDefaultView(_logEntries);
        BindingOperations.EnableCollectionSynchronization(_logEntries, _lock);
    }

【问题讨论】:

  • 能否请您发布您目前工作的代码?如果你做得好,即使有大量数据,你的应用也能顺利运行。
  • 请同时发布您填充ObservableCollection 的代码(如果您正在使用)以及您的DataGrid 的XAML 定义。
  • 好吧,我更新了,希望对你有帮助^^
  • 预渲染是可能的。获取项目数(直接或以预测/适应方式),用空条目填充它(即使看到它们也不会产生输出,如项目的占位符),然后异步加载,调用对 ObservableCollection 的更改并触发更改,因此视图可以更新内容。我希望虚拟化会忽略对不可见事物的更新,所以总的来说这应该比完全同步加载更快。
  • @Sinatr 谢谢队友我会试试的

标签: c# wpf performance datagrid logfile


【解决方案1】:

从文件中读取,您应该更改为:

XAML:

<DataGrid
          Grid.Row="1"
      x:Name="DataGrid"
      Grid.ColumnSpan="2"
      Margin="5"
      IsReadOnly="True"
      AutoGenerateColumns="False"
      CanUserReorderColumns="False" ItemsSource="{Binding}">
    <DataGrid.Columns>
        <DataGridTemplateColumn Width="Auto">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Image Source="{Binding Path=LogLevelIcon, Mode=OneWay}" Width="16" Height="16"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTextColumn Width="Auto" Header="Datum" Binding="{Binding Path=DateTime, Mode=OneWay}"/>
        <DataGridTextColumn Width="*" Header="Meldung" Binding="{Binding Path=Message, Mode=OneWay}"/>
    </DataGrid.Columns>
</DataGrid>

C#代码:

Dispatcher DP = Dispatcher.CurrentDispatcher;

public void ExtractDataFromLogFile(string logFilePath)
{
    new Thread(() =>
    {
        var lines = File.ReadAllLines(logFilePath);
        foreach (var line in lines) ProcessFileContent(line);
        DP.Invoke(() => DataGrid.DataContext = _logEntries);
    }).Start();
}

private void ProcessFileContent(string line)
{
    Match match = _regex.Match(line);
    if (match.Success)
    {
        LogEntry entry = new LogEntry()
        {
            LogLevel = match.Groups[1].ToString(),
            DateTime = DateTime.Parse(match.Groups[2].ToString(), new CultureInfo("de-DE")),
            Message = match.Groups[3].ToString()
        };
        _logEntries.Add(entry);
    }
}

如果您对_logEntries 有任何绑定,请立即将其删除。

如下调用:

ExtractDataFromLogFile("yourLogFile");

这会将您的数据加载到_logEntries,完成后,它会自动绑定到DataGrid

【讨论】:

  • 问题是我需要实时更新文件。
  • 所以当我的应用程序编写一个新条目时,我希望它立即显示
  • 不错看起来不错。另一个问题是我需要使用 CollectionView 实现的条目的过滤器选项。更换 CollectionView 时,我该怎么做呢?很抱歉造成这么多工作;(
  • 如果我的回答不能解决你的问题,我会改进的。如果它解决了,你的新问题应该是创建新问题,因为每个问题使用应该只发布一个问题,那么我会尽力帮助你!
  • 它不工作...我不能使用File.ReadAllLines(logFilePath),因为日志文件已经“打开”了。我可以将 FileMode 之类的东西与 File.ReadAlLines 一起使用吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多