Lifeupdate DataGrid из TextFile с хорошей производительностью

Текущее состояние:

У меня DataGrid с 4 столбцами (значок | DateTime | LogLevel | Сообщение)

Я использую его как средство просмотра, чтобы представить записи о LogFile. При открытии Window пользовательский интерфейс задерживается, и множество записей добавляются одна за другой к DataGrid.

Примечание. Я уже использую несколько потоков. Моя UI-Thread не зависает. Просто нужно слишком много времени, чтобы заполнить весь DataGrid.

Чего я хочу:

Я бы предпочел что-то вроде «предварительного рендеринга» всего окна, прежде чем показывать его пользователю.

Когда я открывал Window один раз - каждый раз, когда я открываю его снова, это больше не проблема. (Не рендеринг нового ....?)

Что я пробовал:

  • Установите Visibility на Hidden и подождите (Thread.Sleep()) 10 секунд, затем установите Visibility = Visibility.Visible;
  • Добавление всех данных в мой DataGrid в ViewModel-Constructor

но все это на самом деле не исправило. Я даже не уверен, это код 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<LogEntry>() _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);                    
        }
    }

Наконец, XAML DataGrid (стили исключены!):

<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);
    }

person Felix D.    schedule 24.02.2016    source источник
comment
Не могли бы вы опубликовать свой код, над которым вы работаете до сих пор? если вы все сделаете правильно, ваше приложение будет работать без сбоев даже с огромным объемом данных.   -  person Tuyen Pham    schedule 24.02.2016
comment
Также опубликуйте код, в котором вы заполняете свой ObservableCollection (если вы его используете), и определение XAML вашего DataGrid.   -  person toadflakz    schedule 24.02.2016
comment
Хорошо, ребята, я обновил его, надеюсь, это поможет ^^   -  person Felix D.    schedule 24.02.2016
comment
Предварительный рендеринг возможен. Получите количество элементов (напрямую или прогнозируемым / адаптируемым образом), заполните его пустыми записями (которые не производят вывода, даже если они видны, например, заполнитель элемента), затем загрузите асинхронно, вызывая изменения в ObservableCollection и активируя изменение, поэтому просмотр может обновлять контент. Я ожидаю, что виртуализация будет игнорировать обновления невидимых вещей, поэтому в целом это должно быть быстрее, чем полностью синхронная загрузка.   -  person Sinatr    schedule 24.02.2016
comment
@Sinatr, спасибо, приятель, я попробую   -  person Felix D.    schedule 24.02.2016
comment
Можете ли вы попробовать отключить все визуальные и другие необычные вещи, которые вы сделали с макетом? Я имею в виду просто полностью снять его, а затем снова протестировать производительность, даже если это будет выглядеть не очень хорошо. Я не так хорошо знаком с WPF, но есть ли у вас какие-либо параметры Best-Fit для столбцов?   -  person Apostrofix    schedule 24.02.2016
comment
@Apostrofix мой ColumnWidth установлен на Авто для всех, кроме сообщения. Этот заполняет ...   -  person Felix D.    schedule 24.02.2016
comment
Можете ли вы сказать мне приблизительный размер и приблизительное количество строк в вашем файле данных?   -  person Tuyen Pham    schedule 24.02.2016
comment
@Sakura В настоящее время (поскольку это файл журнала) его только 10 КБ и 107 строк = ›107 LogFileEntrys с DateTime и 3 строковыми свойствами каждый - по мере того, как становится все хуже, рендеринг ухудшается ..   -  person Felix D.    schedule 24.02.2016
comment
хорошо, подожди минутку   -  person Tuyen Pham    schedule 24.02.2016


Ответы (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.

person Tuyen Pham    schedule 24.02.2016
comment
Проблема в том, что мне нужны Live-обновления файла. - person Felix D.; 24.02.2016
comment
Поэтому, когда мое приложение пишет новую запись, я хочу, чтобы она отображалась немедленно. - person Felix D.; 24.02.2016
comment
Красиво хорошо выглядит. Другая проблема заключается в том, что мне нужна опция фильтра для записей, это достигается с помощью CollectionView. Как я могу это сделать при замене CollectionView? Извините за столько работы; ( - person Felix D.; 25.02.2016
comment
Если мой ответ не решит вашу проблему, я исправлю его. Если он решен, ваш новый вопрос должен быть создан новым вопросом, так как каждый вопрос должен содержать только одну проблему, тогда я постараюсь вам помочь! - person Tuyen Pham; 25.02.2016
comment
Не работает ... Я не могу использовать File.ReadAllLines(logFilePath), поскольку файл журнала всегда открыт. Могу ли я использовать что-то вроде FileMode с File.ReadAlLines? - person Felix D.; 25.02.2016
comment
Почему ты всегда открываешь его? Если вы хотите добавить строку в конец текстового файла, используйте File.AppendAllText или File.AppendAllLines. Или для переопределения используйте File.WriteAllText или File.WriteAllLines, вам не нужно открывать его как поток, это больше похоже на двоичные данные. - person Tuyen Pham; 25.02.2016
comment
Потому что не удается добавить строку, если LogViewer открыт; ( - person Felix D.; 25.02.2016
comment
Вы где-нибудь используете какой-либо файловый поток? Если у него есть, опубликуйте его, и я могу помочь вам преобразовать его в подходящий способ. - person Tuyen Pham; 25.02.2016