Я пишу приложение WPF
(шаблон MVVM
с использованием MVVM Light Toolkit
) для чтения и отображения набора внутренних файлов журнала, используемых моей компанией.Цель состоит в том, чтобы читать из нескольких файлов, извлекать содержимое из каждой строки, помещать их в объект класса и добавлять указанный объект в ObservableCollection
.Я установил ItemsSource
из DataGrid
в моем GUI
в этот список, чтобы он отображал данные в аккуратных строках и столбцах.У меня есть элемент управления ProgressBar
во втором окне, которое во время процесса чтения и отображения файла будет обновлять ход выполнения.
Настройка
Обратите внимание, что все эти методы сокращены до удаления основныхвсе нерелевантные биты кода.
Кнопка загрузки
Когда пользователь выбирает каталог, содержащий файлы журнала, и нажимает эту кнопку, процесс начинается.Я открываю окно, которое содержит ProgressBar
на данный момент.Я использую BackgroundWorker
для этого процесса.
public void LoadButtonClicked()
{
_dialogService = new DialogService();
BackgroundWorker worker = new BackgroundWorker
{
WorkerReportsProgress = true
};
worker.DoWork += ProcessFiles;
worker.ProgressChanged += Worker_ProgressChanged;
worker.RunWorkerAsync();
}
ProcessFiles () Метод
Это читает все файлы в выбранном каталоге,и обрабатывает их один за другим.Здесь, при запуске окна индикатора выполнения, я использую Dispatcher.Invoke()
.
private void ProcessFiles(object sender, DoWorkEventArgs e)
{
LogLineList = new ObservableCollection<LogLine>();
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
_dialogService.ShowProgressBarDialog();
});
var fileCount = 0;
foreach (string file in FileList)
{
fileCount++;
int currProgress = Convert.ToInt32(fileCount / (double)FileList.Length * 100);
ProcessOneFile(file);
(sender as BackgroundWorker).ReportProgress(currProgress);
}
}
ProcessOneFile () Метод
Это, как следует из названия, читаетодин файл, проходя построчно, преобразует содержимое в объекты моего класса и добавляет их в список.
public void ProcessOneFile(string fileName)
{
if (FileIO.OpenAndReadAllLinesInFile(fileName, out List<string> strLineList))
{
foreach (string line in strLineList)
{
if (CreateLogLine(line, out LogLine logLine))
{
if (logLine.IsRobotLog)
{
LogLineList.Add(logLine);
}
}
}
}
}
Так что все работает отлично и отображает мои журналы так, как я хочу.
Проблема
Однако, после их отображения , если я прокручиваю свой DataGrid
, GUI
зависает и выдает мне следующее исключение.
System.InvalidOperationException: 'ItemsControl несовместим с источником своих элементов.См. Внутреннее исключение для получения дополнительной информации. '
Прочитав об этом в SO и с помощью Google, я понял, что это потому, что мой LogLineList
несовместим с ItemsSource
, которыйприводит к конфликту.
Текущее решение
Я обнаружил, что если я добавлю строку кода в ProcessOneFile
, где я добавлю объект класса в свой список внутривторой Dispatcher.Invoke()
это решает мою проблему.Вот так:
if (logLine.IsRobotLog)
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
LogLineList.Add(logLine);
});
}
Теперь это снова работает нормально, но проблема в том, что это ужасно замедляет время обработки.Если раньше файл журнала с 10 000 строк занимал около 1 с, то теперь он может занимать в 5-10 раз больше времени.
Я что-то не так делаю, или это следовало ожидать?Есть ли лучший способ справиться с этим?