Life 从具有良好性能的文本文件更新 DataGrid
Lifeupdate DataGrid from TextFile with good performance
实际状态:
我有一个 DataGrid
有 4 列 (Icon|DateTime|LogLevel|Message)
我将它用作查看器来显示 LogFile
的条目。
打开 Window
时,UI 滞后,大量条目被一一添加到 DataGrid
。
注意:我已经在使用多线程了。我的 UI-线程没有冻结。它只是需要很长时间才能填满整个 DataGrid
.
我想要的:
在向用户展示之前,我更喜欢 "pre-render" 整个 window。
当我打开 Window
一次时 - 每次我再次打开它时,都没有问题了。(不渲染新的....?)
我试过的:
- 将
Visibility
设置为 Hidden
并等待 (Thread.Sleep()
) 10 秒,然后设置 Visibility = Visibility.Visible
;
- 在 ViewModel-Constructor
中将所有数据添加到我的 DataGrid
但这一切并没有真正解决问题。我什至不确定它是 C# 代码还是只是绑定...
这可能是一个愚蠢的问题,但有没有办法在显示之前 "pre-render" DataGrid
及其 Content
?
编辑:
我也使用一些 DataTriggers
来设置 RowColor 但这可能不是问题..
这是我使用的一些代码:
The Entry Class:
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}";
}
}
Getting the Data from my LogFile:
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();
}
Adding to the 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);
}
}
Finally the XAML of the DataGrid (Styles left out!):
<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"是typeof ICollectionView
我这里填写:
private void InitializeCollection()
{
ItemsView = CollectionViewSource.GetDefaultView(_logEntries);
BindingOperations.EnableCollectionSynchronization(_logEntries, _lock);
}
从文件中读取,您应该更改为:
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
。
实际状态:
我有一个 DataGrid
有 4 列 (Icon|DateTime|LogLevel|Message)
我将它用作查看器来显示 LogFile
的条目。
打开 Window
时,UI 滞后,大量条目被一一添加到 DataGrid
。
注意:我已经在使用多线程了。我的 UI-线程没有冻结。它只是需要很长时间才能填满整个 DataGrid
.
我想要的:
在向用户展示之前,我更喜欢 "pre-render" 整个 window。
当我打开 Window
一次时 - 每次我再次打开它时,都没有问题了。(不渲染新的....?)
我试过的:
- 将
Visibility
设置为Hidden
并等待 (Thread.Sleep()
) 10 秒,然后设置Visibility = Visibility.Visible
; - 在 ViewModel-Constructor 中将所有数据添加到我的
DataGrid
但这一切并没有真正解决问题。我什至不确定它是 C# 代码还是只是绑定...
这可能是一个愚蠢的问题,但有没有办法在显示之前 "pre-render" DataGrid
及其 Content
?
编辑:
我也使用一些 DataTriggers
来设置 RowColor 但这可能不是问题..
这是我使用的一些代码:
The Entry Class:
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}";
}
}
Getting the Data from my LogFile:
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();
}
Adding to the
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);
}
}
Finally the XAML of the DataGrid (Styles left out!):
<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"是typeof ICollectionView
我这里填写:
private void InitializeCollection()
{
ItemsView = CollectionViewSource.GetDefaultView(_logEntries);
BindingOperations.EnableCollectionSynchronization(_logEntries, _lock);
}
从文件中读取,您应该更改为:
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
。