C# - 将大文件加载到 WPF RichTextBox 中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/837086/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# - Loading a large file into a WPF RichTextBox?
提问by
I need to load a ~ 10MB range text file into a WPF RichTextBox, but my current code is freezing up the UI. I tried making a background worker do the loading, but that doesnt seem to work too well either.
我需要将一个 ~ 10MB 范围的文本文件加载到 WPF RichTextBox 中,但我当前的代码冻结了 UI。我尝试让后台工作人员进行加载,但这似乎也不太好。
Here's my loading code. Is there any way to improve its performance? Thanks.
这是我的加载代码。有什么办法可以提高它的性能吗?谢谢。
//works well for small files only
private void LoadTextDocument(string fileName, RichTextBox rtb)
{
System.IO.StreamReader objReader = new StreamReader(fileName);
if (File.Exists(fileName))
{
rtb.AppendText(objReader.ReadToEnd());
}
else rtb.AppendText("ERROR: File not found!");
objReader.Close();
}
//background worker version. doesnt work well
private void LoadBigTextDocument(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
System.IO.StreamReader objReader = new StreamReader( ((string[])e.Argument)[0] );
StringBuilder sB = new StringBuilder("For performance reasons, only the first 1500 lines are displayed. If you need to view the entire output, use an external program.\n", 5000);
int bigcount = 0;
int count = 1;
while (objReader.Peek() > -1)
{
sB.Append(objReader.ReadLine()).Append("\n");
count++;
if (count % 100 == 0 && bigcount < 15)
{
worker.ReportProgress(bigcount, sB.ToString());
bigcount++;
sB.Length = 0;
}
}
objReader.Close();
e.Result = "Done";
}
回答by AlaaShaker
Why don't you add to a string variable (or perhaps even use StringBuilder) then assign the value to the .Text property when you're done parsing?
为什么不添加到字符串变量(或者甚至可能使用 StringBuilder)然后在完成解析后将值分配给 .Text 属性?
回答by ist_lion
Have you considered trying to make the app multi-threaded?
您是否考虑过尝试使应用程序成为多线程的?
How much of the text file do you need to see at once? You may want to look into lazy-loading in .NET or in your case C#
您需要一次查看多少文本文件?您可能想研究 .NET 中的延迟加载或在您的情况下 C#
回答by Guffa
Graphical controls just isn't designed to handle that kind of data, simply because it would become unworkable. Even if the control could handle the large string, what's visible in the control is so little compared to the entire text that the scroll bars would become practically useless. To locate a specific line in the text you would have to move the slider to the closest position that it could specify, then scroll a line at a time for minutes...
图形控件并不是设计用来处理这种数据的,只是因为它会变得不可行。即使控件可以处理大字符串,与整个文本相比,控件中可见的内容也很少,滚动条实际上变得毫无用处。要在文本中找到特定行,您必须将滑块移动到它可以指定的最近位置,然后一次滚动一行几分钟......
Instead of submitting your users to something useless like that, you should rethink how you display the data, so that you can do it in a way that would actually be possible to use.
与其将您的用户提交给这样无用的东西,您应该重新考虑如何显示数据,以便您可以以实际可以使用的方式进行显示。
回答by David Basarab
I have notice using RichTextboxes that as you add more "lines" it starts to slow down. If you can do it without appending the '\n' it will speed up for you. Remember each '\n' is a new paragraph object block for the RichTextbox.
我注意到使用 RichTextboxes 当您添加更多“行”时,它开始变慢。如果您可以在不附加 '\n' 的情况下执行此操作,它将为您加快速度。请记住,每个 '\n' 都是 RichTextbox 的一个新段落对象块。
This is my method for loading a 10 MB file. It takes about to 30 seconds to load. I use a progress bar dialog box to let my user know it is going to take time to load.
这是我加载 10 MB 文件的方法。加载大约需要 30 秒。我使用进度条对话框让我的用户知道加载需要时间。
// Get Stream of the file
fileReader = new StreamReader(File.Open(this.FileName, FileMode.Open));
FileInfo fileInfo = new FileInfo(this.FileName);
long bytesRead = 0;
// Change the 75 for performance. Find a number that suits your application best
int bufferLength = 1024 * 75;
while (!fileReader.EndOfStream)
{
double completePercent = ((double)bytesRead / (double)fileInfo.Length);
// I am using my own Progress Bar Dialog I left in here to show an example
this.ProgressBar.UpdateProgressBar(completePercent);
int readLength = bufferLength;
if ((fileInfo.Length - bytesRead) < readLength)
{
// There is less in the file than the lenght I am going to read so change it to the
// smaller value
readLength = (int)(fileInfo.Length - bytesRead);
}
char[] buffer = new char[readLength];
// GEt the next chunk of the file
bytesRead += (long)(fileReader.Read(buffer, 0, readLength));
// This will help the file load much faster
string currentLine = new string(buffer).Replace("\n", string.Empty);
// Load in background
this.Dispatcher.BeginInvoke(new Action(() =>
{
TextRange range = new TextRange(textBox.Document.ContentEnd, textBox.Document.ContentEnd);
range.Text = currentLine;
}), DispatcherPriority.Normal);
}
回答by David Basarab
I'm working on a very similar project.
我正在做一个非常相似的项目。
The project entails loading a large text file (max size approx: 120MB but we want to go higher) and then constructing an outline of the text file in a tree. Clicking on a node in the tree will scroll the user to that portion of the text file.
该项目需要加载一个大文本文件(最大大小约为:120MB,但我们想要更高),然后在树中构建文本文件的轮廓。单击树中的节点会将用户滚动到文本文件的该部分。
After talking to a lot of people I think the best solution is to create a sort of "sliding window" viewer where you only load as much text as the user can see at a time into the rtb.Text.
在与很多人交谈后,我认为最好的解决方案是创建一种“滑动窗口”查看器,您只能将用户一次可以看到的文本加载到 rtb.Text 中。
So.. say load the entire file into a List but only put 100 of those lines into rtb.Text. If the user scrolls up remove the bottom line and add a line of text to the top. If they scroll down remove the top line and add a line of text to the bottom. I get pretty good performance with this solution. (50s to load a 120MB file)
所以..说将整个文件加载到一个列表中,但只将其中的 100 行放入 rtb.Text。如果用户向上滚动删除底线并在顶部添加一行文本。如果他们向下滚动,请删除顶行并在底部添加一行文本。我使用此解决方案获得了非常好的性能。(加载 120MB 文件需要 50 秒)
回答by coco
You can try this it worked for me.
你可以试试这个它对我有用。
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Create new StreamReader
StreamReader sr = new StreamReader(openFileDialog1.FileName, Encoding.Default);
// Get all text from the file
string str = sr.ReadToEnd();
// Close the StreamReader
sr.Close();
// Show the text in the rich textbox rtbMain
backgroundWorker1.ReportProgress(1, str);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// richTextBox1.Text = e.ProgressPercentage.ToString() + " " + e.UserState.ToString();
richTextBox1.Text = e.UserState.ToString();
}
回答by Franck Charlier
I'm not improve the performance of loading, but I use it to load my richtextbox asynchronously. I hope that could help you.
我没有提高加载的性能,但我用它来异步加载我的富文本框。我希望这可以帮助你。
XAML :
XAML:
<RichTextBox Helpers:RichTextBoxHelper.BindableSource="{Binding PathFileName}" />
Helper :
帮手:
public class RichTextBoxHelper
{
private static readonly ILog m_Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(RichTextBoxHelper), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string)obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var thread = new Thread(
() =>
{
try
{
var rtfBox = o as RichTextBox;
var filename = e.NewValue as string;
if (rtfBox != null && !string.IsNullOrEmpty(filename))
{
System.Windows.Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Background,
(Action)delegate()
{
rtfBox.Selection.Load(new FileStream(filename, FileMode.Open), DataFormats.Rtf);
});
}
}
catch (Exception exception)
{
m_Logger.Error("RichTextBoxHelper ERROR : " + exception.Message, exception);
}
});
thread.Start();
}
}
回答by Harshil
WPF RichTextBox control use Flow Document to display Rich Text and then attach the Flow Document to RTB control,while Windows Form RichTextBox control display Rich Text directly. that's what makes WPF RTB super slow. if you are okay with using a WinForm RTB just host it in your wpf app. the xaml :
WPF RichTextBox 控件使用 Flow Document 显示富文本,然后将 Flow Document 附加到 RTB 控件,而 Windows Form RichTextBox 控件直接显示富文本。这就是 WPF RTB 超慢的原因。如果您可以使用 WinForm RTB,只需将其托管在您的 wpf 应用程序中即可。xaml :
<Window x:Class="WpfHostWfRTB.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<Grid>
<WindowsFormsHost Background="DarkGray" Grid.row="0" Grid.column="0">
<wf:RichTextBox x:Name="rtb"/>
</WindowsFormsHost>
</Grid>
</Grid>
</Window>
C# code
C# 代码
private void LoadTextDocument(string fileName, RichTextBox rtb)
{
System.IO.StreamReader objReader = new StreamReader(fileName);
if (File.Exists(fileName))
{
rtb.AppendText(objReader.ReadToEnd());
}
else rtb.AppendText("ERROR: File not found!");
objReader.Close();
}