C # - загрузка большого файла в WPF RichTextBox? - PullRequest
6 голосов
/ 08 мая 2009

Мне нужно загрузить текстовый файл с диапазоном ~ 10 МБ в WPF RichTextBox, но мой текущий код зависает в интерфейсе. Я попытался заставить фонового работника выполнить загрузку, но это тоже не слишком хорошо работает.

Вот мой код загрузки. Есть ли способ улучшить его производительность? Спасибо.

    //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";
    }

Ответы [ 8 ]

3 голосов
/ 28 мая 2014

Элемент управления WPF RichTextBox использует потоковый документ для отображения Rich Text, а затем присоединяет потоковый документ к элементу управления 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();
}
3 голосов
/ 08 мая 2009

Графические элементы управления просто не предназначены для обработки таких данных просто потому, что они стали бы неработоспособными. Даже если элемент управления может обрабатывать большую строку, то, что видно в элементе управления, настолько мало по сравнению со всем текстом, что полосы прокрутки станут практически бесполезными. Чтобы найти определенную строку в тексте, вам нужно переместить ползунок в самое близкое положение, которое он может указать, а затем прокрутить строку за раз в течение минут ...

Вместо того, чтобы отправлять своих пользователей на что-то бесполезное, вы должны переосмыслить способ отображения данных, чтобы вы могли сделать это так, как это возможно.

2 голосов
/ 27 июня 2009

Я работаю над очень похожим проектом.

Проект предполагает загрузку большого текстового файла (максимальный размер около: 120 МБ, но мы хотим пойти выше), а затем построение контура текстового файла в дереве. Нажав на узел в дереве, вы прокрутите пользователя до этой части текстового файла.

Поговорив со многими людьми, я думаю, что лучшее решение - создать своего рода средство просмотра "скользящего окна", в которое вы загружаете только столько текста, сколько пользователь видит за раз в rtb.Text.

Итак ... скажем, загрузите весь файл в список, но поместите только 100 из этих строк в rtb.Text. Если пользователь прокручивает вверх, удалите нижнюю строку и добавьте строку текста вверху. Если они прокручиваются вниз, удалите верхнюю строку и добавьте строку текста к нижней. Я получаю довольно хорошую производительность с этим решением. (50 секунд для загрузки файла размером 120 МБ)

1 голос
/ 12 июня 2009

Я заметил, что с помощью RichTextboxes, когда вы добавляете больше «строк», он начинает замедляться. Если вы можете сделать это без добавления '\ n', это ускорит вас. Помните, что каждый символ \ n - это новый объектный блок абзаца для RichTextbox.

Это мой метод загрузки файла размером 10 МБ. Загрузка занимает около 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);
}
1 голос
/ 08 мая 2009

Почему бы вам не добавить строковую переменную (или, возможно, даже не использовать StringBuilder), а затем присвоить значение свойству .Text, когда вы закончите анализ?

0 голосов
/ 24 октября 2013

Я не улучшаю производительность загрузки, но использую ее для асинхронной загрузки своего 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();
}
}
0 голосов
/ 29 июня 2011

Вы можете попробовать это, у меня получилось.

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();
}
0 голосов
/ 08 мая 2009

Рассматривали ли вы попытку сделать приложение многопоточным?

Какую часть текстового файла вам нужно увидеть одновременно? Вы можете посмотреть ленивую загрузку в .NET или в вашем случае C #

...