2009-05-07 22 views
6

Necesito cargar un archivo de texto de rango ~ 10MB en un WPF RichTextBox, pero mi código actual congela la IU. Intenté hacer que un trabajador de segundo plano realizara la carga, pero tampoco parece funcionar demasiado bien.C# - ¿Cargando un archivo grande en un WPF RichTextBox?

Aquí está mi código de carga. ¿Hay alguna forma de mejorar su rendimiento? Gracias.

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

Respuesta

1

¿Por qué no añades a una variable de cadena (o tal vez incluso el uso de StringBuilder) A continuación, asigne el valor de la propiedad .Texto cuando haya terminado el análisis?

+0

está hablando el primer bloque de código o el segundo? –

-1

¿Ha considerado intentar hacer la aplicación de subprocesos múltiples?

¿Cuánto del archivo de texto necesita ver a la vez? Es posible que desee considerar la carga diferida en .NET o en su caso C#

3

Los controles gráficos simplemente no están diseñados para manejar ese tipo de datos, simplemente porque se volverían inviables. Incluso si el control puede manejar la cadena grande, lo que está visible en el control es tan pequeño en comparación con el texto completo que las barras de desplazamiento se volverían prácticamente inútiles. Para ubicar una línea específica en el texto, debe mover el control deslizante a la posición más cercana que pueda especificar, luego deslice una línea a la vez durante minutos ...

En lugar de enviar a sus usuarios a algo inútil como ese , debe reconsiderar cómo visualiza los datos, para que pueda hacerlo de una manera que realmente sea posible utilizar.

+3

No es algo irrazonable preguntar. Puedo abrir archivos multi-GB en UltraEdit sin problema. No es RTF, claro, pero muchos IDEs comerciales manejan archivos en el rango de los 10MB razonablemente bien, y recuerden que tienen que analizar la sintaxis del lenguaje para obtener los bonitos colores, no solo leer el formato preprocesado. –

+1

@Richard: Sí, hay programas que pueden manejar archivos grandes, pero no hay un simple control de GUI. Editar una cadena tan grande significa que debe copiar 20 GB de datos por cada pequeño cambio, por lo que el control se volverá extremadamente lento. Los programas que manejan archivos grandes usan una forma diferente de representar los datos en la memoria. – Guffa

+0

@downvoter: ¿Por qué el voto a favor? Si no explica qué es lo que cree que está mal, no puede mejorar la respuesta. – Guffa

1

He notado el uso de RichTextboxes que a medida que agrega más "líneas" comienza a disminuir la velocidad. Si puede hacerlo sin agregar el '\ n', se acelerará. Recuerde que cada '\ n' es un nuevo bloque de objetos de párrafo para RichTextbox.

Este es mi método para cargar un archivo de 10 MB. Tarda unos 30 segundos en cargarse. Utilizo un cuadro de diálogo de la barra de progreso para que mi usuario sepa que llevará tiempo cargar.

// 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); 
} 
+0

Es '\ n ', no n'. – markwatson

+0

¡Usted señor, es un campeón! – Derek

2

Estoy trabajando en un proyecto muy similar.

El proyecto implica cargar un archivo de texto grande (tamaño máximo aprox: 120MB pero queremos aumentar) y luego construir un esquema del archivo de texto en un árbol. Al hacer clic en un nodo en el árbol, el usuario se desplazará a esa parte del archivo de texto.

Después de hablar con mucha gente, creo que la mejor solución es crear una especie de visor de "ventana deslizante" donde solo cargue la cantidad de texto que el usuario pueda ver a la vez en el texto rtb.

Así que ... cargue todo el archivo en una lista pero solo ponga 100 de esas líneas en rtb.Text. Si el usuario se desplaza, elimine la línea inferior y agregue una línea de texto en la parte superior. Si se desplazan hacia abajo, elimine la línea superior y agregue una línea de texto en la parte inferior. Obtuve un rendimiento bastante bueno con esta solución. (Años 50 para cargar un archivo de 120 MB)

+0

Todos los editores que admiten archivos realmente grandes funcionan mostrando un número limitado de páginas a la vez. También usan sus propios controles personalizados que pueden manejar grandes cantidades de texto. Hay controles de terceros que * soportan * virtualización de datos (cargando y representando solo la parte del texto que se muestra) –

0

Puede probar esto funcionó para mí.

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(); 
} 
-1

No mejoro el rendimiento de la carga, pero lo uso para cargar mi richtextbox de forma asincrónica. Espero que pueda ayudarte.

XAML:

<RichTextBox Helpers:RichTextBoxHelper.BindableSource="{Binding PathFileName}" /> 

Ayudante:

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(); 
} 
} 
2

WPF RichTextBox control del uso del Flujo de documentos para visualizar texto enriquecido y luego adjuntar el documento de flujo de control de RTB, mientras la pantalla de control de Windows Forms RichTextBox de texto enriquecido directamente. eso es lo que hace que WPF RTB sea súper lento. si está de acuerdo con el uso de un WinForm RTB, simplemente hágalo en su aplicación wpf. el 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ódigo 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(); 
} 
+0

Había agotado las opciones de hilos de fondo y otros hackers de WPF RichTextBox: si tenía un AppendText o uno por línea, o incluso establecí una selección y cargué RTF sin procesar en ella, fue lento como un perro. Usando su configuración ilustrativa, cambié de WPF a winforms y vi más que una aceleración de orden de magnitud, y el código para manipular e indexar el texto también se volvió más simple. ¡Muchas gracias de hecho! –

Cuestiones relacionadas