[英]C# - Loading a large file into a WPF RichTextBox?
我需要將一個~10MB范圍的文本文件加載到WPF RichTextBox中,但我當前的代碼正在凍結UI。 我嘗試讓后台工作人員進行加載,但這似乎也不太好用。
這是我的加載代碼。 有沒有辦法改善其表現? 謝謝。
//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";
}
WPF RichTextBox控件使用Flow Document顯示Rich Text,然后將Flow Document附加到RTB控件,而Windows Form RichTextBox控件直接顯示Rich Text。 這就是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#代碼
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();
}
圖形控件並不是為處理那種數據而設計的,僅僅因為它變得不可行。 即使控件可以處理大字符串,控件中可見的內容與整個文本相比也是如此之少,以至於滾動條實際上變得無用。 要在文本中找到特定的行,您必須將滑塊移動到它可以指定的最近位置,然后一次滾動一行幾分鍾......
您應該重新考慮如何顯示數據,以便您可以以實際可以使用的方式執行此操作,而不是將用戶提交給無用的用戶。
我正在做一個非常相似的項目。
該項目需要加載一個大文本文件(最大大小約120MB,但我們想要更高),然后在樹中構建文本文件的大綱。 單擊樹中的節點將用戶滾動到文本文件的該部分。
在與很多人交談之后,我認為最好的解決方案是創建一種“滑動窗口”查看器,您只需將用戶一次可以看到的文本加載到rtb.Text中。
所以..說將整個文件加載到List中,但只將100行放入rtb.Text。 如果用戶向上滾動,請刪除底行並在頂部添加一行文本。 如果他們向下滾動刪除頂行並在底部添加一行文本。 我用這個解決方案獲得了不錯的表現。 (加載120MB文件50秒)
我注意到使用RichTextboxes,當你添加更多“行”時,它開始變慢。 如果您可以在不附加'\\ n'的情況下執行此操作,它將為您加速。 請記住每個'\\ n'是RichTextbox的新段落對象塊。
這是我加載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);
}
為什么不添加到字符串變量(或者甚至可能使用StringBuilder),然后在解析完成后將值分配給.Text屬性?
你可以試試這個對我有用。
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();
}
我沒有提高加載的性能,但我使用它來異步加載我的richtextbox。 我希望能幫助你。
XAML:
<RichTextBox Helpers:RichTextBoxHelper.BindableSource="{Binding PathFileName}" />
幫手:
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();
}
}
您是否考慮過嘗試使應用程序成為多線程?
您需要立即查看多少文本文件? 您可能希望在.NET中或在您的情況下考慮延遲加載C#
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.